[HN Gopher] New inline assembly syntax available in Rust nightly
       ___________________________________________________________________
        
       New inline assembly syntax available in Rust nightly
        
       Author : dagenix
       Score  : 370 points
       Date   : 2020-06-09 14:10 UTC (8 hours ago)
        
 (HTM) web link (blog.rust-lang.org)
 (TXT) w3m dump (blog.rust-lang.org)
        
       | m0zg wrote:
       | Is there anything like https://github.com/herumi/xbyak on the
       | Rust side? I like this approach a lot for non-tivial high
       | performance work since it lets you tweak your assembly for
       | particular hardware at runtime.
        
       | jfkebwjsbx wrote:
       | Anybody that knows about the RFC and other implementations in C
       | compilers, why are always "string literal parameters" (quoted
       | strings) used?
       | 
       | Wouldn't be nice to have a way to embed other languages without
       | the need for those quotes everywhere?
       | 
       | I guess it is meant to avoid complicating the syntax/grammar
       | reusing the existing macro one?
        
         | dathinab wrote:
         | Yes it adds a lot of complexity on the tokenization level (and
         | others), potentially requiring some form of contextual
         | tokenization.
         | 
         | Scala originally supported "inline" XML but moved it into some
         | form of compiler plugin you can opt-in to as just having it
         | enabled had drawbacks even if you don't use any XML... (I don't
         | remember the exact drawbacks anymore).
        
         | Sharlin wrote:
         | For maximum generality and compatibility, likely. Also note
         | that this is "just" a macro, not first-class language syntax,
         | so any non-quoted arguments would have to be valid Rust token
         | trees.
        
           | est31 wrote:
           | > any non-quoted arguments would have to be valid Rust token
           | trees.
           | 
           | Token trees allow a very wide set of syntaxes. rust-c for
           | example allows you to embed inline C code as token tree via a
           | macro: https://github.com/lemonrock/rust-c
        
         | [deleted]
        
         | kevincox wrote:
         | Yes, Rust macros have certain syntax requirements that allow
         | the code to parse without knowing what a macro does (and to
         | find out where a macro ends). Don't quote me but there are some
         | token requirements as well as balanced parenthesis. If you
         | allow another language to be inserted raw you risk that it
         | requires syntax that would break these requirements.
         | 
         | Buy using a raw string literal you have a well defined quoting
         | syntax mediating the two languages.
        
         | geofft wrote:
         | It means that a parser for Rust doesn't also have to be a
         | parser for every embedded language - which is particularly
         | important for things like syntax highlighters in text editors
         | or HTML renderers or whatever.
         | 
         | For example, in most languages including Rust, parentheses are
         | balanced as a rule, but they might not be in some inner
         | language. So if you want to define a syntax where you can say
         | inner_language!( smiley = :-) );
         | 
         | while you can certainly write a parser that understands what's
         | going on there, any existing Rust parser will get very
         | confused. So, unless you're confident the inner language is
         | syntactically compatible (even if it's meaningless) with the
         | outer language, you'd need to protect it somehow.
         | 
         | Making the inner language quoted means that existing parsers
         | that already know how to handle the outer language's string
         | syntax (where to find the close quote, how close quotes might
         | be escape, etc.) work seamlessly. You could also define some
         | other syntax for it, but it's fundamentally the same
         | requirement - it's a well-defined open and close syntax - and
         | most other syntaxes end up uglier (think of <script>//<!-- ...
         | //--></script or <script>//<![CDATA[ ... //]]></script> in
         | HTML/XHTML.)
         | 
         | And there's nothing preventing a particularly clever parser
         | from recognizing the asm! macro specifically and syntax-
         | highlighting (or otherwise tokenizing/processing/etc.) the text
         | inside, if it wants to.
        
         | [deleted]
        
         | klyrs wrote:
         | Zig has a nice solution for that:
         | 
         | https://ziglang.org/documentation/master/#Global-Assembly
         | 
         | The "magic" being that lines beginning with double-backslashes
         | are string literals.
        
           | masklinn wrote:
           | That's a solution in none of the ways GP asks about. It's
           | just a different syntax for a string literal, the entire
           | point of the original comment is _why string literals_.
           | 
           | > The "magic" being that lines beginning with double-
           | backslashes are string literals.
           | 
           | So... strictly worse than rust which allows linebreaks inside
           | string literals, and thus only require a dquote before and
           | one after the entire block instead of a two-char prefix
           | before each line?
        
         | jcranmer wrote:
         | In principle, you can embed languages without the need for
         | strings. However, when you do so, you need to worry about the
         | tokenization of languages to see that a) they combine in a
         | sensible way and b) tools won't get confused if they don't
         | understand the sub-language (this is a real issue that cropped
         | with XHTML and JS).
         | 
         | In practice, inline assembly has pretty much been seen as a
         | "paste this text into the generated assembly of the program,
         | after substituting some values determined by register
         | allocation"--in other words, it's a pretty natural string
         | formatting problem, and describing the language like you would
         | a formatted string is a natural step to take.
         | 
         | Another point to make is that assembly languages are wildly
         | divergent in their actual syntax. ARM uses # to denote
         | literals, x86 (Intel syntax) uses # to denote comments.
        
         | mhh__ wrote:
         | It saves time (there also isn't a single ground truth for
         | parsing assembler)
         | 
         | https://dlang.org/spec/iasm.html
         | 
         | No quotes required
        
       | mhh__ wrote:
       | I don't understand (I do actually but it's not that much work)
       | why so few languages do what D does and actually have inline
       | assembly as a fully supported (no quotes) syntactic construct
       | within the language
        
         | cowboysauce wrote:
         | > I do actually but it's not that much work
         | 
         | It apparently is, given that D only supports x86 assembly,
         | doesn't support all instructions and has idiosyncrasies like
         | requiring prefixes to be written as separate instructions.
        
           | mhh__ wrote:
           | D only has about <20 people working on it at a given time.
           | Some languages probably have at least a hundred times that
           | number.
        
       | artursapek wrote:
       | If I wanted to learn assembly language in 2020, what's the best
       | book or resource people would recommend?
        
         | thomspoon wrote:
         | Imo there's not really a best book or resource other than
         | buying a microcontroller and doing it yourself! Pick up a ARM
         | Cortex-M4 development board either from TI or STM and
         | reimplement their example code or driver code in assembly! You
         | can look at the ISA to see what assembly instructions are
         | supported, or the disassembly of a running program (probably
         | with no optimizations so you can see what they are doing)
        
           | jagger27 wrote:
           | There's definitely something to be said about poking a
           | physical pin in assembly. It makes me feel like I actually
           | have control of the computer, and that I'm not just along for
           | the ride.
        
         | ethagnawl wrote:
         | https://famicom.party features the best, most accessible 6502
         | assembly crash course I've yet to come across.
         | 
         | It does strike me as weird that there aren't any popular
         | resources that sit between something as high-level as the
         | linked e-book and something as low-level as "buy some hardware
         | and figure it out".
        
           | mywittyname wrote:
           | I suspect this has to do with Intel architecture not being
           | very beginner friendly. When I tried learning x86 assembly, I
           | found it to be incredibly overwhelming.
           | 
           | There are simulators for teaching assembly, but I don't think
           | those are nearly as fulfilling as hacking on real hardware.
           | And since it's difficult to hack on your computer's hardware,
           | you're left pickup up something more simple.
        
         | adamnemecek wrote:
         | I really like "Introduction to 64 Bit Assembly Programming for
         | Linux and OS X: For Linux and OS X" by Ray Seyfarth. It's short
         | and you actually implement something interesting like the
         | correlation function or basic data structures, like linked
         | list.
        
       | JoeAltmaier wrote:
       | Always tricky. I avoid inline assembler, except in cases where
       | the compiler cannot generate some special instruction. This can
       | occur in drivers and kernel code, where there's no other language
       | feature to manipulate processor architecture (special registers
       | or machine state manipulation).
       | 
       | It can be fun to try and hand-optimize code in regular
       | applications. I can't help but feel, there's more juice in
       | teaching the compiler to recognize the case and generate better
       | code instead. But few know how to go down that road.
        
       | [deleted]
        
       | Bedon292 wrote:
       | I am super new to Rust and maybe not the best place to ask, but
       | why would you want to use inline assembly in Rust at all? Does
       | that not invalidate a lot of the safety features built into the
       | language?
        
         | oconnor663 wrote:
         | There are some places where you have to use assembly
         | instructions:
         | 
         | - Operating systems. For example, the voodoo that happens
         | during early boot, where you need to walk the CPU through
         | different modes. And also fundamental kernel stuff like mapping
         | memory.
         | 
         | - Cryptography. Crypto code is often hand-written in assembly
         | for efficiency. (Sometimes it's even the other way around:
         | crypto algorithms may be designed with specific machine
         | instructions in mind.) Also there are places where you have to
         | guarantee that some operation happens in constant time, which
         | is hard to do when a compiler may choose to insert branches or
         | jumps without telling you.
         | 
         | To compete with C in those spaces, Rust will need inline
         | assembly features.
         | 
         | As far as safety is concerned, yes, inline assembly is
         | completely unsafe. In that sense, it's not so different from
         | calling functions in the C standard library (or any other C
         | library), which might do absolutely anything with your memory.
         | In all of these cases, the Rust programmer has to use the
         | `unsafe` keyword, and it's up to them to make sure that Rust's
         | rules are still respected after the unsafe code has run. Doing
         | this properly, and wrapping it all in a safe Rust API, means
         | that other safe code can then use your library without the
         | `unsafe` keyword and without any risk of triggering undefined
         | behavior.
        
           | mywittyname wrote:
           | > - Operating systems. For example, the voodoo that happens
           | during early boot,
           | 
           | Isn't every system call an assembly instruction? Not just the
           | voodoo stuff?
           | 
           | My only experience with "real" assembly was my OS class in
           | college, in which a project we had involved adding system
           | calls to Linux, and they were all snippets of assembly code
           | called in C.
        
             | DSMan195276 wrote:
             | Yes, generally speaking you need inline assembly to execute
             | whatever architecture-specific instruction is used to enter
             | the kernel (`int 0x80`, `syscall`, `swi`, etc.). And in the
             | kernel you need inline assembly for other various
             | architecture-specific instructions so you can execute them
             | as part of your regular code instead of having to write
             | them in a separate assembly file.
             | 
             | For the weirder cases like right after boot or handling an
             | interrupt you generally just have to go full assembly for
             | that, since in general you're not started out in a state
             | where you can just start running your typical C/Rust/etc.
             | code. It depends heavily on your platform at that point
             | though.
        
         | codys wrote:
         | "safely abstracting `unsafe`" is the important concept with
         | respect to rust and it's use of `unsafe`.
         | 
         | `unsafe` occurs in rust source code where the author of some
         | code needs to do things in a way that can't be directly proven
         | to the compiler to be safe. These include things like calling C
         | functions and ASM code (both cases where we can't infer all the
         | information necessary to ensure safety). The author of the
         | unsafe code then provides an "safe" abstraction around the
         | unsafety that ensures that when one uses the "safe" interface,
         | no undefined behavior occurs.
         | 
         | At the lowest level there is always some unsafety: system
         | calls, libc function invocations, asm, modifying various memory
         | mapped registers. What rust provides vs C or C++ is effective
         | isolation of the unsafety.
        
         | [deleted]
        
         | metalliqaz wrote:
         | If you have a large-ish project that is otherwise coded in
         | Rust, but you have to do some low-level coding because of
         | special I/O, or special intrinsic features for multimedia, or
         | even just flat-out performance, then this allows you to do so
         | without falling back on linking against ASM or C.
        
         | learc83 wrote:
         | Rust wants to be a serious contender for low level and embedded
         | code. In those spaces sometimes you don't have much of a choice
         | but to drop down into assembly.
        
         | woodruffw wrote:
         | Rust has lots of unsafe features; they all require explicit
         | `unsafe {}` blocks.
         | 
         | As the post mentions, inline assembly comes in handy in a
         | number of low level contexts (e.g. dealing with memory-mapped
         | devices, working on microcontrollers, using processor features
         | that aren't exposed by the kernel or standard library).
        
           | masklinn wrote:
           | > e.g. dealing with memory-mapped devices, working on
           | microcontrollers, using processor features that aren't
           | exposed by the kernel or standard library
           | 
           | Or kernel features not exposed by the libc.
        
           | gizmo385 wrote:
           | > using processor features that aren't exposed by the kernel
           | or standard library).
           | 
           | As someone who doesn't do any low-level work, could you give
           | an example of these kinds of features? I'd like to read into
           | some of them :)
        
             | neutronicus wrote:
             | Some processors implement "Instruction Level Parallelism"
             | where you can do arithmetic on many values simultaneously
             | with special assembly instructions.
             | 
             | The most widespread cases of this (SSE, AVX2) came from
             | Intel trying to get lock-in in the high-performance
             | computing market. So it took a while for AMD to sell chips
             | that implemented these instructions and even Intel's
             | catalog doesn't uniformly offer them.
             | 
             | It's also tough to get the compiler to emit these
             | instructions where you want even if it knows they're
             | available (unsurprisingly, since you're asking it to auto
             | parallelize a computation), so in the high-performance
             | space a lot of people just have to resort to inline
             | assembly.
        
               | jlokier wrote:
               | Just a minor terminology point. Instruction Level
               | Parallelism (ILP) means something else.
               | 
               | What you are describing, with special instructions, is
               | called Single Instruction Multiple Data (SIMD), or simply
               | vector instructions.
               | 
               | ILP means the execution of multiple separate instructions
               | in parallel per clock cycle, though techniques like
               | superscalar, out-of-order and speculative execution. ILP
               | is sometimes used as a measure: How many instructions per
               | cycle can be issued. It does not require special
               | instructions, it's just the hardware being clever about
               | running existing instructions faster.
        
               | neutronicus wrote:
               | Ah, we used that wrong in my old group then.
               | 
               | We talked about Instruction Level Parallelism vs. Thread
               | Level Parallelism vs. Node Level Parallelism since
               | generally if you are working on a problem with some data
               | dependency you will really have to think about each
               | separately to get the best performance.
        
               | masklinn wrote:
               | This sort of instructions you often have intrinsics for
               | though.
        
               | neutronicus wrote:
               | Ah, yeah, in my head I tend to conflate intrinsics and
               | inline assembly
        
             | woodruffw wrote:
             | My go-to example of this is CPU-level virtualization
             | support, like AMD-V or VT-x.
             | 
             | Some C/C++ compilers will expose these instructions as
             | intrinsics, but AFAIK Rust doesn't.
        
             | JoshTriplett wrote:
             | Moving into or out of a control register, such as to enable
             | a processor feature. Disabling or enabling interrupts.
             | Reading or writing model-specific registers. Saving
             | registers, switching stacks, and calling another function,
             | then switching back and restoring registers when the
             | function returns. Initializing and using hardware
             | virtualization features (e.g. Intel VT). Using features
             | like SMAP (preventing accidental access from the kernel to
             | user memory through a wild pointer, temporary enabling it
             | in careful dedicated routines like copy_from_user and
             | copy_to_user). Making raw system calls from userspace.
        
             | fluffything wrote:
             | Just take any operating systems course and try to implement
             | your own.
             | 
             | Or try to program a micro-controler "hello world" that
             | actuates an LED ?
        
               | masklinn wrote:
               | Aren't those _usually_ MMIO?
        
             | AndrewGaspar wrote:
             | I used inline assembly once when playing around with Rust
             | on GPUs to get Cuda's implicit thread ID and grid ID:
             | https://docs.nvidia.com/cuda/parallel-thread-
             | execution/index...
        
         | dmytrish wrote:
         | Embedded/systems programming is not possible without dropping
         | to assembly at times. Also, it can unlock some hardware-
         | specific performance optimizations.
         | 
         | Rust already has unsafe blocks (explicitly marked `unsafe`) for
         | this kind of situations. Inline assembly obviously is allowed
         | only in unsafe contexts and should be used very carefully.
        
         | jfkebwjsbx wrote:
         | Yes, but low-level programming implies direct access to memory
         | and instructions.
         | 
         | Rust is not designed for memory safety _only_. If you only want
         | that, there are other simpler options, like any functional,
         | scripting or managed language. Instead, Rust is designed to
         | bring as much memory safety as possible (but not more!) to the
         | low-level and performance fields.
        
         | neutronicus wrote:
         | Because ultimately Rust wants to be a C++ replacement and you
         | can't do that if you don't let people who know what they're
         | doing opt into inline assembly and raw pointers.
        
       | amluto wrote:
       | Rust folks, thank you so much for making this far, far better
       | than GCC's asm syntax for C. Also, thank you for using Intel x86
       | syntax instead of AT&T.
       | 
       | It would be delightful if GCC were to adopt something similar
       | after this stabilizes in Rust.
        
         | pjmlp wrote:
         | Still fails short of asm code blocks of PC based languages.
        
           | FpUser wrote:
           | Second that. Very simple example:                 function
           | MultiplyBy2Reg(aInput: int64): int64 assembler
           | register;       asm         SHL RAX, 1;       end;
           | function MultiplyBy2(aInput: int64): int64 assembler;
           | asm         mov RAX, aInput;         SHL RAX, 1;       end;
        
             | simias wrote:
             | Now I need to port that code to MIPS, what do?
             | 
             | This is a very nice feature to have if you know your
             | environment is overwhelmingly tied to a certain
             | architecture and you want to make it easy to target this
             | architecture in particular.
             | 
             | This might actually be a somewhat reasonable, if short-
             | sighted approach if we were in 2005 and you could make the
             | point that anything that's not x86 is effectively legacy or
             | niche but nowadays you need to at the very least support
             | x86-64, ARM32 and AARCH64. Baking all these flavors of
             | assembly into a language would be very heavy handed and a
             | maintenance nightmare.
             | 
             | Beyond that we're not in the 90's anymore, ASM is not
             | routinely used outside of very low level code these days.
             | Even for performance people often opt for intrinsics
             | instead. Compilers got massively better at optimizing code
             | while humans got worse due to ever more complex
             | architectures.
             | 
             | Being able to seamlessly blend ASM with normal code isn't
             | that much of a killer feature anymore IMO. It would be a
             | costly gimmick to implement.
        
               | FpUser wrote:
               | ifdef is your friend. used everywhere
        
               | pjmlp wrote:
               | It is Assembly after all, just use conditional
               | compilation.
        
           | systems wrote:
           | what are PC based languages ? what is PC short for in this
           | context
        
             | pjmlp wrote:
             | MS-DOS/Windows heritage.
             | 
             | Modula-2, Pascal, C, C++, Basic based compilers with inline
             | Assembly parsers or intrisics that interact with host
             | language type system instead of manually dealing with
             | strings UNIX style.
        
               | dathinab wrote:
               | But in rust asm! _does_ interact with the type system.
               | 
               | The asm macro does know about what group of registers (or
               | what specific register) a input/output is stored/loaded
               | from and compilation will fail if the input isn't
               | compatible with the register.
               | 
               | Then it's delegating to a assembler for the rest.
               | 
               | Sure it could also have a list of all asm commands the
               | given target does support and parameters/registers it can
               | be used with but this means keeping track of all
               | supported targets and all commands for all potential
               | featuresets of all targets and all way they can be
               | called. This is a lot of work. Even more such support can
               | be added later one, too. For now it's more important to
               | support asm on stable.
               | 
               | Also with proc-macros you can implement this as a library
               | on top of `asm`!
               | 
               | Also with a more "knowledgeable" asm any support for a
               | new platform would need to manually add support for all
               | asm of that language so having a standard stringly
               | interface is a good think anyway.
        
               | jfkebwjsbx wrote:
               | What you describe is how it works in GCC/LLVM and is
               | required in any inline asm system. Otherwise it is not
               | really inline.
               | 
               | Saying that fits with the type system is a stretch... it
               | is like any other FFI.
        
               | Rusky wrote:
               | The use of strings in Rust's implementation is limited to
               | the assembly template and the operand constraints.
               | Functionally speaking, moving those out of strings gains
               | you absolutely nothing and can make parsing more
               | difficult.
               | 
               | The important part, type system integration, is already
               | there. Sizes must match, move and borrow checking still
               | happens, etc. This avoids the typical failure modes of
               | unix-like stringly-typed systems. In fact if you read the
               | announcement and RFC, you'll see that this was a primary
               | goal of the new design!
        
           | simias wrote:
           | I've followed the discussion regarding how ASM should be
           | integrated in Rust over the past few years, the idea of
           | integrating the ASM syntax more tightly with Rust has been
           | brought up. Basically one could treat assembly as an ad-hoc
           | domain specific language with its own parsing rules that
           | would make it more like a first class citizen instead of a
           | magical character string. I've actually implemented a very
           | simplistic MIPS assembler in a similar way for emulation
           | purposes: https://github.com/simias/rustation/blob/master/src
           | /parallel...
           | 
           | I think they were absolutely right not to go down that route,
           | because then you have to basically specify this syntax for
           | every possible architecture targeted by Rust. Integrating ASM
           | in the language syntax is fine if you only care about x86,
           | but it's going to be a mess if you want to support MIPS,
           | ARM32, AARCH64, Sparc and whatnot because the ASM dialects
           | for each of these architectures have special bespoke syntax
           | to deal with various quirks of the underlying ISA. MIPS has
           | special syntax to load the top and bottom half of an address
           | as well as dealing with delay slots (.noreorder), ARM has
           | special sugar for PC-relative addressing etc...
           | 
           | Importing that baggage into Rust would be a fool's errand
           | IMO. It would also make it harder both to port Rust to new
           | architectures _and_ to transfer existing assembly code into
           | Rust since it would require adjusting the syntax.
           | 
           | I'm very happy with Rust's current approach, it's a good
           | middle ground IMO.
        
         | JoshTriplett wrote:
         | > Also, thank you for using Intel x86 syntax instead of AT&T.
         | 
         | Thank you for helping to validate that decision; this is the
         | kind of feedback we needed.
         | 
         | (And note that you can still choose to use AT&T syntax, it just
         | isn't the default.)
        
           | mzs wrote:
           | I prefer AT&T, thank you for the option.
        
           | jdright wrote:
           | Yes, thank you for the Intel syntax. It is by far the most
           | frequent syntax you'll find anywhere somewhat recent (1990+)
           | and it is by far the most straightforward to learn by being
           | clean. I did learn both in the 90s, but I've always struggled
           | with AT&T symbols and different offerings which to me was
           | hard to switch to when learning from different material
           | sources.
        
           | exmadscientist wrote:
           | If you're ever in doubt on which syntax to implement first or
           | make default, just look at the processor manual (TRM). Match
           | it, and you'll have an ironclad defense :)
        
             | JoshTriplett wrote:
             | That was one of our primary justifications. The tradeoff
             | was between that and what's currently widely used with GCC
             | and clang; the latter (along with people porting from
             | what's now called llvm_asm!) motivated us adding the
             | att_syntax option.
        
           | binarycrusader wrote:
           | Yes, this is great, but I have to disagree on the Intel x86
           | syntax assertion by the original poster. Perhaps it's because
           | of the era I grew up in and how I first learned machine
           | language and then assembly language, but I've always
           | preferred AT&T syntax. Regardless thanks for supporting the
           | saner syntax even if it's not by default ;)
        
         | josephcsible wrote:
         | Is AT&T syntax really that bad? In particular, it's way nicer
         | for specifying operand sizes. I'd prefer "andl $3, (%eax)" any
         | day over "and DWORD PTR [eax], 3".
        
           | dmytrish wrote:
           | I subjectively like AT&T syntax more (as a more familiar to
           | me and having more "machine" vibe).
           | 
           | However, I recognize that it's objectively worse for a human
           | coder and contains some syntactic footguns which shoot even a
           | experienced coder regularly:
           | 
           | - `number` (memory displacement/pointer) used instead of
           | literal `$number`. It's an easy automatic mistake even for a
           | person who knows this very well.
           | 
           | - the SIB clauses for x86 (memory addressing with constant
           | Displacement and Scale and Index, Base in registers) look
           | like `D(B, I, S)`: it's possible to remember this, but
           | reading/writing it is not as obvious as `[D + B + I * S]`.
           | 
           | - Intel syntax in general is more similar to high-level
           | languages, even though it's more verbose.
           | 
           | - AT&T syntax has syntactic redundancies like '%' before each
           | register which make code much noisier than needed.
        
             | pwdisswordfish2 wrote:
             | % before register names isn't redundant: it allows you to
             | refer to symbols that would otherwise have the same
             | spelling as register names.
        
               | dmytrish wrote:
               | Yes, that's where AT&T is more logical.
               | 
               | In practice, though, naming a symbol with a register name
               | is error-prone and confusing. I'd rather have a way to
               | escape a symbol when it's really needed than to pay the
               | price of noise for an admittedly bad idea.
        
               | cesarb wrote:
               | New registers are added all the time (for instance, the
               | %zmm registers), so even if a symbol name doesn't
               | conflict now, it might conflict in the future. Having
               | separate namespaces for symbols and registers is a good
               | idea.
        
             | hyperman1 wrote:
             | The intel syntax has some nice gotchas though as the scale
             | can be 1. I believe there is [eax+ebp] which uses the ds
             | segment, but [ebp+eax] uses ss.
        
               | exmadscientist wrote:
               | The really fun bit is when you explicitly want to force
               | one form or other, for some reason. Usually this is
               | related to instruction length, but occasionally you used
               | to run into times when one form could help avoid a stall.
               | 
               | Keeping tricks like that stable and clearly documented
               | is, uh, not for the faint of heart.
        
           | fluffything wrote:
           | The problem is not whether its bad or not, but rather, having
           | to learn a completely-different second syntax for one
           | architecture.
           | 
           | Most Rust code is quite portable, targeting ARM, x86, MIPS,
           | PPC, WASM, Sparc, s390x, riscv, ... That means, that for many
           | snippets of inline assembly, you might encounter ~8 of them,
           | one for each architecture, all using different syntaxes.
           | 
           | Intel syntax is quite similar to that of other popular
           | architectures `op dst, args...`.
           | 
           | Adding another second syntax for x86 just doesn't add that
           | much value IMO, and adds quite a bit of cost: now everybody
           | dealing with x86 assembly needs to learn 2 syntaxes... and
           | everybody dealing with portable code now needs to be at least
           | able to read 2 syntaxes for x86... Without talking about the
           | cost of implementing a second syntax in the compiler, etc.
           | 
           | If you prefer AT&T, you can always write a proc macro that
           | translates it to Intel, and use that in your projects.
           | 
           | If I ever need to deal with such code, I'd just expand the
           | macro to read the actual Intel syntax, modify that, and
           | either fork the project, or submit a patch with a fix using
           | Intel syntax.
        
             | amluto wrote:
             | I have two big reasons for preferring Intel syntax:
             | 
             | 1. It's the syntax in the manual. The last thing I want to
             | do when reading or writing asm is to mentally translate
             | from the manual to AT&T syntax.
             | 
             | 2. Addressing like (%rax) is tolerably. But the AT&T scale
             | * index + offset syntax is inexcusable. Give me the verbose
             | Intel addressing syntax any day, please.
             | 
             | (As a kernel programmer, I'm more familiar with AT&T. I
             | _still_ hate it. I'm morbidly curious how Intel syntax
             | ought to handle things like SGDT. Maybe SGDT SIXBYTE PTR
             | [address]? The fact that four bytes is called a DWORD isn't
             | great.)
        
               | jlebar wrote:
               | > 1. It's the syntax in the manual. The last thing I want
               | to do when reading or writing asm is to mentally
               | translate from the manual to AT&T syntax.
               | 
               | Right! I worked on C++ compilers for years and I don't
               | even know where is the canonical book of AT&T mnemonics.
               | At the rate that Intel is adding new instructions, using
               | anything other than their official docs (and thus their
               | official names) seems nuts.
        
             | jfkebwjsbx wrote:
             | AT&T syntax is actually the common one in most low-level
             | programming if you count by architectures and most likely
             | also by code size produced, if only because GCC has/had
             | been the de facto compiler for new chip uarchs for more
             | than a decade (helped by the fact that everyone wants their
             | chips to run Linux).
             | 
             | Nowadays GCC and LLVM support both styles and archs pick
             | whatever they prefer and nobody cares, really.
             | 
             | > either fork the project, or submit a patch with a fix
             | using Intel syntax.
             | 
             | That sounds a bit extreme? Reading/writing in both styles
             | is not an issue for anyone that has dealt with x86
             | professionally.
        
               | oblio wrote:
               | And apparently Rust already supports both.
        
               | exmadscientist wrote:
               | >AT&T syntax is actually the common one in most low-level
               | programming
               | 
               | Is this actually true? Admittedly I've done mostly x86
               | and ARM for the past several years (almost entirely
               | Cortex-M, so v6-M and v7-M profiles, using ARM's GCC
               | builds for embedded) and the only toolchain that prefers
               | AT&T syntax is x86 GCC and those explicitly trying to be
               | compatible with it. All the ARM inline assembly I've
               | written, targetting GCC backends, has been ARM syntax,
               | and likewise for all the disassembly output.
               | 
               | The DSPs and DSP-likes are... always weird. So I try to
               | stay away from them and make them someone else's problem.
               | But I don't think they use AT&T syntax either. It doesn't
               | work so well for truly strange processors anyway.
               | 
               | I'm one of those guys who tends to have the makefiles
               | output the disassembly, and have it open on the other
               | monitor while I'm working, so I'd notice if it were
               | different....
        
             | JoshTriplett wrote:
             | > If you prefer AT&T, you can always write a proc macro
             | that translates it to Intel, and use that in your projects.
             | 
             | If you prefer AT&T (or you have a large body of existing
             | AT&T code you don't want to have to translate all at once),
             | use asm!("...", options(att_syntax)) and it'll Just Work.
        
           | rectang wrote:
           | They're both terrible. :( I yearn for a more ergonomic
           | assembler.
           | 
           | That's not to say that the Rust team should have tried to
           | find (or create) an alternative; it's outside their core
           | mission, and choosing between popular existing alternatives
           | is the right framing.
        
         | simias wrote:
         | I agree. They took their time but was worth it. I really missed
         | not having had ASM is stable Rust the past few years but since
         | it means not having to deal with something as horrendous as
         | GCC's __asm__ syntax I'm glad they took their time.
        
         | bobly_today wrote:
         | As someone who only knows at&t syntax I guess I should view
         | this is an opportunity to learn intel syntax
        
       | Teknoman117 wrote:
       | To be honest, I still like the explicit cobber syntax of
       | rusty_asm.
       | 
       | Last year I had some fun getting a rust program to run on some
       | old 386 DOS SBC I had hanging around. Was terrible in terms of
       | code density (basically a bunch of 32 bit instructions prefixed
       | with the "execute 32 bit op in real mode" prefix).
       | 
       | This was a wrapper around the DOS string print function (where s
       | is a &str).                       rusty_asm! {
       | let ptr: in("{dx}") = s.as_ptr();                 let len:
       | in("{cx}") = s.len();                 clobber("ah");
       | clobber("al");                 clobber("bx");
       | asm("volatile", "intel") {                     "mov ah, 0x40
       | xor bx, bx                     int 21h"                 }
       | }
        
         | JoshTriplett wrote:
         | In the new syntax, a direct translation of that would look
         | something like this:                   asm!("             mov
         | ah, 0x40             xor bx, bx             int 21h
         | ",             in("dx") s.as_ptr(),             in("cx")
         | s.len(),             out("ax") _,             out("bx") _,
         | )
         | 
         | However, I'd personally write that like this instead:
         | asm!(             "int 21h",             inout("ax") 0x4000 =>
         | _,             inout("bx") 0 => _,             inout("cx")
         | s.len() => _,             inout("dx") s.as_ptr() => _,
         | )
         | 
         | That lets the compiler handle filling in ax and bx, and also
         | avoids trusting that the interrupt routine leaves ax/bx/cx/dx
         | untouched. (I might also write clobbers for every other
         | general-purpose register, too. Take a look at Linux
         | arch/x86/boot/bioscall.S and the "Glove box" for BIOS calls,
         | due to misbehaving BIOSes that clobber registers they
         | shouldn't.)
        
           | Teknoman117 wrote:
           | Thanks! The latter of the two is definitely nice. Yeah, I
           | should've let the compiler setup ax and bx. Again, this was a
           | hack :)
        
       | livre wrote:
       | I may be the only one but I feel like this (and the old) asm
       | syntax is too complex and not very user friendly. It looks
       | heavily inspired GCC. I'm sorry for the upcoming German (you can
       | ignore it and just read the examples), I feel like a smarter
       | compiler could have a friendlier asm syntax, similar to the one
       | used by FreeBASIC[1].
       | 
       | I hope you can see why I prefer that. Even though the FB compiler
       | is not very smart and just pushes and pops all important regs to
       | and from the stack its asm syntax is a joy to write. I don't have
       | to specify clobber registers, in, out and variables and functions
       | are in scope by default. Also the asm code is not a string and
       | doesn't need to be quoted, it's doesn't look like something
       | foreign at all.
       | 
       | I know that something like that is currently not possible in Rust
       | but I'd love to see it some day.
       | 
       | [1] https://www.freebasic-portal.de/befehlsreferenz/inline-
       | assem...
        
         | jcranmer wrote:
         | You can track some of the history of the proposals here:
         | https://github.com/rust-lang/rust/issues/29722.
         | 
         | A lot of the improvements you've suggested have been suggested
         | in the past, but there's some reasons why they aren't being
         | proposed. In short, sufficiently smart compilers can probably
         | guess at a lot of constraints automatically, but building that
         | level of smarts is very daunting, and for most use cases, the
         | user probably wants/needs the fine-grained control anyways.
         | 
         | For example, take the call instruction. Which registers should
         | the compiler mark that as using and/or clobbering? It depends
         | on the ABI of the function being called--and the user might not
         | even be calling a function at all (say, using a call as a
         | gadget to get the current program counter). Saving a register
         | is another common technique in inline assembly, and you don't
         | want the compiler to infer that the register is clobbered--
         | indeed you're saving it to explicitly not clobber it.
        
         | fluffything wrote:
         | > I don't have to specify clobber registers,
         | 
         | So how does the compiler know that these are clobbered then ?
        
           | masklinn wrote:
           | I expect the entire assembly is encoded into the language.
        
             | livre wrote:
             | Not really, the other reply is right. Most registers are
             | clobbered. FreeBASIC only passes the asm code inside the
             | asm block directly to GAS (using the .intel_syntax noprefix
             | directive). As I said, the compiler is too dumb to figure
             | out what registers are being used so as a safety measure it
             | clobbers almost all of them (see Register Preservation
             | here[1]).
             | 
             | [1] https://documentation.help/FreeBASIC/KeyPgAsm.html
        
             | int_19h wrote:
             | That doesn't really help - the compiler doesn't know what
             | could be clobbered by any given (sys)call.
        
           | cesarb wrote:
           | Since the parent comment said "just pushes and pops all
           | important regs", it probably assumes that all registers are
           | clobbered.
        
         | zozbot234 wrote:
         | > I feel like a smarter compiler could have a friendlier asm
         | syntax, similar to the one used by FreeBASIC[1].
         | 
         | Feel free to propose a better syntax, then. This feature is not
         | yet part of Stable Rust, so syntax improvements are very much
         | possible.
         | 
         | The FreeBASIC syntax seems problematic though, since it would
         | require architecture-specific smarts in the compiler and make
         | it hard to port it to different architectures or add support
         | for newer asm features. It might be better to have these
         | additional features as separate tooling or crates, that might
         | even wrap the asm! syntax itself via proc macros.
        
           | livre wrote:
           | Sadly I think Rust's way is the best one in the long term
           | even if it's not very user friendly. There are a lot of
           | problems with the FreeBASIC syntax, my comment was mostly
           | wishful thinking. As other user has said in a reply to my
           | comment, you can't beat the level of control Rust's syntax
           | provides and that's what most people writing assembly would
           | want. You are also right that a proper asm parser would be
           | difficult to maintain due to the amount of architectures Rust
           | has to support.
        
             | dathinab wrote:
             | Yes, I mean we shouldn't forget that asm today is a last
             | resort escape hatch which should be avoided if possible.
             | 
             | EDIT: Just to be clear it's still important. But it's
             | mainly used for a view "very high control" use cases.
        
             | fluffything wrote:
             | Notice that you can quite easily implement a Rust proc
             | macro that takes whatever DSL you want to use for inline
             | assembly, and generates an `asm!` expression from it.
             | 
             | So better syntaxes are possible, as long as they can be
             | built on top of the proposed inline-assembly feature.
             | 
             | They can, however, just be implemented as normal Rust
             | libraries, and do not need to be part of the compiler.
        
         | rwmj wrote:
         | The problem with baking the asm syntax into the language is
         | that it can make it really hard to use new / obscure
         | instructions. And yet that is often the only reason to use
         | inline asm in the first place, because you need some specific
         | CPU feature which the language you're using doesn't expose.
        
       | MintelIE wrote:
       | Is Rust ever going to go for standardization? Seems like the
       | language has been in a state of flux with heavy changes all the
       | time. When's it going to settle down a bit?
        
         | sedatk wrote:
         | As long as it's backwards compatible, why should that be a
         | problem?
        
         | simias wrote:
         | Keep in mind that the changes since 1.0 have been with very few
         | exceptions completely backward compatible. I write a
         | significant amount of Rust and I don't follow the development
         | of the language ultra closely anymore, yet it doesn't feel like
         | I'm coding for a moving target.
         | 
         | Basically from my point of view the changes are about adding
         | features (inline ASM in this case, async I/O previously) but it
         | doesn't really change the way you code, it's just an other tool
         | in your belt that you may decide to use later. If you didn't
         | have a need for inline ASM before you still won't be using it,
         | if you couldn't use Rust previously because of bad ASM support
         | it opens up new use cases.
        
       | mkchoi212 wrote:
       | This looks a lot better than GCC's asm. However, I can't think of
       | a good reason why one should use asm in their code instead of
       | letting the compiler write the far superior asm for you.
        
         | fluffything wrote:
         | Because compilers almost never write better code than humans ?
         | 
         | There are literally tens of thousands of bugs in the GCC and
         | LLVM/Clang bug trackers about that.
         | 
         | I don't remember the last time I read some compiler-generated
         | asm and thought "wow, that's great code".
        
         | yoshuaw wrote:
         | One example is authoring USDT probes. This is done by writing
         | inline assembly comments [1] that the kernel can then trace
         | when instructed to.
         | 
         | Also people doing cryptography things occasionally need access
         | to specific ASM instructions for their work. I believe AES has
         | specialized instructions, but there are more cases.
         | 
         | You're not wrong that writing inline ASM is a fairly
         | specialized need, but for those who require it this will come
         | in incredibly useful.
         | 
         | [1]: I still need to try using this, so I might be wrong. I was
         | waiting on inline assembly to be available before trying tho,
         | so quite excited it's now available.
        
           | mkchoi212 wrote:
           | Yeah true. But doesn't Rust provide an equivalent to C's
           | intrinsic functions like popcount?
        
             | MaulingMonkey wrote:
             | Yes:
             | 
             | https://doc.rust-lang.org/core/intrinsics/fn.ctpop.html
             | 
             | https://doc.rust-lang.org/std/?search=count_ones
             | 
             | There are still use cases for raw assembly untouched by the
             | optimizer - e.g. constant time cryptography functions
             | designed to thwart timing attacks might desire control over
             | the _exact_ instruction stream. Although, most of that can
             | be tackled with out-of-line assembly - as one must do with
             | x64 MSVC for example.
        
         | ratww wrote:
         | Most application developers don't need assembly, but it's still
         | necessary for the people writing Rust and its stdlib itself,
         | embedded developers, people writing libraries that use very
         | specialised optimisations, SIMD, etc.
        
         | Twirrim wrote:
         | Compilers are great, but they don't always produce optimal
         | code. You can quite often produce faster code if you're
         | prepared to hand roll the assembly code, but it's rarely
         | something most people will need to do. The time it takes to
         | produce often outweighs any advantage you'd get, but having it
         | as an option is going to be very important.
         | 
         | Just within the rust world,
         | https://www.nickwilcox.com/blog/autovec2/ from a few weeks ago
         | talks about the limitations with the compiler around
         | vectorisation, demonstrating that with hand-rolled assembly
         | they were able to cut the runtime in half, vs what the compiler
         | was able to produce.
         | 
         | Outside of the rust world, you'll often see hand-rolled
         | assembly in performance critical code across a spectrum of
         | uses, e.g. OpenSSL uses it extensively for performance reasons,
         | Go has it in a number of places in their libraries, with a pure
         | go fall-back, and so on.
        
           | mkchoi212 wrote:
           | Ah gotcha. I guess I just haven't come across a situation
           | that calls for hand written ASM yet :p
        
             | Twirrim wrote:
             | I definitely haven't for the things I'm working on. Good
             | enough is good enough.
             | 
             | edit: Thinking about it, that's not true. I was dealing
             | with some hot code running on some MIPS architecture stuff
             | a while back and the compiler's generated ASM was merely
             | "okay", for the standard library. Alternative libraries
             | with hand-rolled ASM ran significantly faster and we were
             | going to leverage those, when another team came up with
             | architectural changes that allowed us to take that away
             | from the MIPS part of the stack.
        
         | SAI_Peregrinus wrote:
         | Sometimes the compiler generates worse ASM (rarely, but it can
         | happen, especially with vector instructions). Sometimes you
         | want to use ASM instructions there are no intrinsics for.
         | Sometimes you want to write some extremely low-level startup
         | code for an OS or microcontroller and simply need assembly
         | since the C/Rust/whatever runtime can't operate yet (eg main
         | memory needs to be initialized).
        
         | adwn wrote:
         | One niche case where you need assembly is if you write a green
         | threads library. At the yield points, you need to save all
         | necessary registers of the current thread, and restore the
         | registers of the thread you're switching to. There's no way you
         | can do this without assembly, because you a) you need to
         | control exactly which machine instructions are emitted in which
         | order, and b) you need to read and write actual registers, not
         | variables or memory locations.
        
         | palmtree3000 wrote:
         | It's not always superior!
         | 
         | For example, I've been working on a big integer library.
         | Addition is a bit annoying, because you need to add 3 numbers
         | (the prior carry bit, and the two digits you're adding) and get
         | out a new carry bit and a resulting digit. This is a bit
         | cumbersome:                       let (res, carry1) =
         | target_digit.overflowing_add(carry as u64);             let
         | (res, carry2) = res.overflowing_add(other_digit);
         | *target_digit = res;             carry = carry1 || carry2;
         | 
         | The resulting assembly is a fairly literal translation. We
         | perform the addition using an add instruction, and extract the
         | carry flag into a register using setb.
         | addq    (%rdi,%rsi,8), %rcx             setb    %r10b
         | addq    (%rdx,%rsi,8), %rcx             setb    %al
         | movq    %rcx, (%rdi,%rsi,8)             orb     %r10b, %al
         | movzbl  %al, %eax
         | 
         | But there's a dedicated instruction for this, adc. adc adds two
         | operands and the carry flag, while itself setting a carry flag.
         | Manually unrolling the loop a bit, I wrote this assembly:
         | shlb $8, {carry}                     adcq 0x00({y0}), {x0}
         | adcq 0x08({y0}), {x1}                     adcq 0x10({y0}), {x2}
         | adcq 0x18({y0}), {x3}                     adcq 0x20({y0}), {x4}
         | adcq 0x28({y0}), {x5}                     setb {carry}
         | 
         | And got a 3x speedup.
        
           | sethhochberg wrote:
           | Thanks for this - its been many years since I've done
           | anything touching assembly, and never outside of an academic
           | context, so when I read conversations like this I'm always
           | curious for concrete examples of how people are actually
           | _using_ this stuff beyond something vague like interacting
           | with VT-x or working on an experimental OS.
        
           | jeffdavis wrote:
           | The article here:
           | 
           | https://news.ycombinator.com/item?id=23351007
           | 
           | specifically recommends against the carry variants of
           | addition, because the instructions are still dependent on
           | each other and don't pipeline well. In other words, it's
           | using the same algorithm, just buried in a single
           | instruction, and that doesn't necessarily make it faster.
           | 
           | Have you considered using a strategy similar to what the
           | article suggests? I think the HN comments also had some
           | additional suggestions.
        
             | adwn wrote:
             | > _that doesn 't necessarily make it faster_
             | 
             | Did you miss the "And got a 3x speedup" part of the post
             | you were replying to? Actual benchmarks of real code always
             | trump theoretical deliberations.
        
               | jeffdavis wrote:
               | Yikes. I was just trying to link to a relevant technique
               | that the author might find helpful.
               | 
               | A big integer library may have many use cases; a
               | benchmark only shows one data point. It's possible that
               | by deferring carry work across more operations he'd see
               | an even bigger improvement.
        
             | palmtree3000 wrote:
             | I was briefly very excited when I read that article,
             | actually. But as devit points out in a sibling comment,
             | that technique is only relevant to cases where you're
             | adding more than 2 numbers.
             | 
             | Multiplication initially seemed like a very promising use
             | case, since it's basically repeated addition. But I'm not
             | super optimistic about that, because I think it's dominated
             | by the alternate optimization of noticing that the product
             | of two 64 bit numbers cannot saturate the high 64 bits of
             | the resulting 128 bit number, which causes carries to be
             | bounded [1].
             | 
             | [1] https://github.com/rocurley/bignum/blob/b45448a156fb910
             | 0ab06...
        
             | devit wrote:
             | The dependency is unavoidable due to the way addition
             | works.
             | 
             | The approach in the article only works if you are adding a
             | lot of numbers together, and then indeed doing carry
             | propagation once at the end is obviously faster.
             | 
             | But of course there is no way that doing the carry
             | propagation yourself on one addition can possibly be faster
             | on a decent CPU that implements add-with-carry efficienly.
        
               | jeffdavis wrote:
               | Maybe it's worth considering an interface to a big int
               | library that can defer carry work across many operations,
               | and then normalizing at the end?
               | 
               | That certainly sounds useful for, e.g., totaling an array
               | of big integers.
        
           | secondcoming wrote:
           | What is Rust's policy for signed/unsigned int overflow? I
           | assume that it's not modulo or else the complier should have
           | generated ADC for you.
           | 
           | I assume you've been using Compiler Explorer a lot?
        
             | masklinn wrote:
             | > What is Rust's policy for signed/unsigned int overflow?
             | 
             | Defined, by default checked in debug mode and unchecked in
             | release, unsigned wraps and signed wraps as two's
             | complement. This can be overridden by explicitly setting
             | overflow-checks in the relevant profile.
             | 
             | It also, separately, has explicitly wrapping, saturating
             | and checking versions of basic arithmetic operations.
        
             | palmtree3000 wrote:
             | overflowing_add, like I was using above, is explicitly
             | wrapping.
             | 
             | I've found it very difficult to provoke rustc into emitting
             | an ADC: the only case where it does so AFAICT is when
             | adding u128s, which are implemented using u64s. Not sure
             | why, except that the shortest function I could think of to
             | emulate ADC is kind of baroque, and it's possible the
             | compiler can't figure it out.
             | 
             | I've mostly been using cpuprofiler[1] and Vtune to
             | simultaneously profile my code and show the assembly. In
             | theory they both provide timing information per-
             | instruction, but I don't really trust it. For the 6 adc
             | instructions above, it shows the number of clock ticks as
             | ranging from 22 million to 3 billion, which doesn't make
             | sense to me. But at least it shows me the assembly!
             | 
             | [1]
             | https://docs.rs/cpuprofiler/0.0.4/cpuprofiler/index.html
        
               | secondcoming wrote:
               | This will change your life!
               | 
               | https://godbolt.org/z/fntvYa
        
         | xyproto wrote:
         | You can't explore undocumented CPU instructions, like this guy
         | is doing: https://youtu.be/jmTwlEh8L7g
        
       | open-paren wrote:
       | I'm new to Rust, so there may be precedent that I don't know, and
       | I'm kinda spoiled because I'm a front-end dev mainly, where the
       | rule is "don't break the web", but doesn't changing the syntax of
       | a macro like that break backwards compatibility?
        
         | knome wrote:
         | > syntax was highly unlikely to ever graduate from nightly to
         | stable Rust, despite being one of the most requested features.
         | 
         | I think it's safe to infer from this line that there was not
         | yet a guarantee of stability in the interface, and that
         | breaking backwards compatibility was therefore fine.
         | 
         | >We renamed the existing asm! to llvm_asm!
         | 
         | They also kept the existing functionality under a different
         | name to ease transition. Current users will need only update
         | the macro name and then transition to the better syntax at
         | their own pace.
        
         | SAI_Peregrinus wrote:
         | Rust has two ways of ensuring forwards compatibility (or
         | incompatibility):
         | 
         | The first is the `stable`/`nightly` system. Anything in
         | `stable` gets compatibility guarantees, anything in `nightly`
         | can change or be removed at any time.
         | 
         | Nightly also has feature-gates: using an unstable feature
         | requires an opt-in to enable it. The old `asm!` syntax which
         | got renamed was only ever in `nightly`.
         | 
         | The compatibility guarantees for `stable` are based on
         | editions: each year there's an edition, your project's
         | `cargo.toml` specifies which edition your code is for, and
         | compilers will continue to support old edition code. New
         | editions might require changes to existing code, but as long as
         | you don't update the edition in your `cargo.toml` you can
         | freely update your compiler (to a new stable version) without
         | worrying about it breaking. You can use crates (packages) from
         | any edition with any other edition, as long as the compiler is
         | new enough to support the latest edition used.
        
         | amalcon wrote:
         | To clarify, "unstable" in rust-speak means "You can use this,
         | but it might completely change or go away, and therefore break
         | your code."
         | 
         | You need to deliberately opt in to unstable features, so you
         | are unlikely to use something like this by accident.
        
           | brandur wrote:
           | Also worth noting that the old syntax was renamed from `asm!`
           | to `llvm_asm!`. It'll still disappear at some point, but
           | existing users of the former on nightly have a very easy way
           | of fixing their build in the meantime.
        
         | SpaceNugget wrote:
         | "don't break the web" type issues are not really applicable to
         | releases of precompiled languages or applications that ship
         | their own interpreters and don't load third party content.
         | 
         | Let's say you are browsing my website. Suddenly, your browser
         | gets an update and prompts you to re-open the window. My site
         | no longer displays correctly due to a backward incompatible
         | change.
         | 
         | My site is the web and your browser broke it.
         | 
         | If you use a native app or service I write in rust 1.35 or
         | whatever and there is a backwards incompatible change in a new
         | version of rust it has no impact on your use of that app at
         | all.
        
           | dtech wrote:
           | backwards compatibility is still highly valued. Code no
           | longer compiling in newer versions of the languages can
           | create huge churn.
           | 
           | It's one of the reasons Java is so widely used, you can still
           | use Java 1.4 libraries in Java 13 and most 1.4 code will keep
           | working and compiling.
        
         | coding_lobster wrote:
         | >I'm a front-end dev mainly, where the rule is "don't break the
         | web"
         | 
         | left-pad flashbacks intensify
        
         | whatshisface wrote:
         | `asm!` never made it out of the nightly builds, so nothing
         | that's being changed has been released.
        
         | dralley wrote:
         | The important thing in this case is that the previous syntax
         | was never actually stabilized, and was only available in
         | nightly releases of the compiler.
         | 
         | So technically it does break some code, but only code which was
         | deliberately opting into unstable features with the knowledge
         | that it may break in the future.
        
         | jcranmer wrote:
         | asm! has been an unstable feature for years, which means a)
         | it's available only in nightly builds, and b) there is no
         | guarantee that it will remain backwards-compatible.
         | 
         | Actually, with regards to asm!, it's been known for a long time
         | that it was unlikely to be stabilized as-is (being originally a
         | very thin wrapper around LLVM's inline asm functionality).
         | Probably nearly everyone who's been relying on inline asm to
         | make their code work has already been following the effort to
         | replace asm! with a better version.
        
           | twic wrote:
           | > Probably nearly everyone who's been relying on inline asm
           | to make their code work has already been following the effort
           | to replace asm! with a better version
           | 
           | Except for my colleague who basically says "nah, they'll
           | never be able to move off the LLVM syntax, the code i'm
           | writing now will be supported forever".
           | 
           | Fortunately, we only have a small amount of assembly, so if
           | we ever do want to upgrade to a version of the compiler which
           | has dropped support, it won't be a lot of work.
        
             | dathinab wrote:
             | Support will likely not be dropped anytime soon, maybe
             | never. You just need to rename it to `llvm_asm!`. Through
             | with `asm!` likely going to be stabilized while `llvm_asm`
             | never going to be stabilized it might be worthwhile to
             | switch to the new asm to get of nightly in the future ;=)
        
           | gpm wrote:
           | c) Even on nightly builds you need to put #![feature(asm)] at
           | the root of your crate to use it. (Likewise for all unstable
           | features, just replace asm with feature_name.)
        
         | trufas wrote:
         | Every couple years they'll define an edition of a language
         | (e.g. rust 2015, rust 2018), the idea being that if you choose
         | to stick to one of them, the latest rust will still compile
         | your code as long as you tell it what version you're
         | targetting.
        
           | JoshTriplett wrote:
           | This isn't related to editions; asm was never stable syntax
           | and couldn't be used in the stable compiler; you could _only_
           | use it on nightly and only if you opted in. This new syntax
           | is something we hope to stabilize eventually.
        
       | dathinab wrote:
       | I recommend reading the RFC. It explains pretty well how it works
       | and it's readable even for people with only very minimal asm
       | experience:
       | 
       | https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-in...
        
       | whatshisface wrote:
       | Hopefully this will help us get better numerical libraries for
       | scientific computing.
        
         | Q6T46nT668w6i3m wrote:
         | I hope so too but I'm skeptical. In my opinion, Rust still
         | needs ergonomic multi-dimensional index expressions and
         | standardized multi-dimension array traits. I'd love to see (or
         | join) a scientific working group to help with this.
        
           | whatshisface wrote:
           | That would be amazing, but I would also be satisfied with a
           | numpy clone. Of course language-level support would be far
           | better!
        
             | Q6T46nT668w6i3m wrote:
             | I can envision something between the two, e.g. a situation
             | similar to the Future trait and the various async backends.
        
           | adrianN wrote:
           | The lack of such features did not prevent numerical libraries
           | in C.
        
             | Q6T46nT668w6i3m wrote:
             | Or Rust (I don't understand your point)
        
               | adrianN wrote:
               | As the topic was inline assembly, I interpreted "better"
               | as "faster", not "more ergonomic".
        
           | devit wrote:
           | You can use a[(i, j, k)], and also a[i][j][k] for some data
           | layouts.
           | 
           | As for multidimensional array traits, you can use
           | Index<(usize, usize, usize), Output = f32>.
        
             | kzrdude wrote:
             | you can use the syntax `a[[i, j, k]]` too, just for another
             | example.
        
         | jcranmer wrote:
         | For that sort of code, the value of inline assembly over
         | compiler intrinsics (and Rust already has stabilized arch-
         | specific intrinsics for the platforms listed in the current
         | inline asm proposal) is likely minimal or nonexistent.
        
       | pjmlp wrote:
       | Sadly it is yet another language that follows the UNIX tradition
       | of not having a proper Assembly parser instead forcing us to play
       | with strings.
       | 
       | I guess it leaves PC based languages to have actual inline
       | Assembly parsers or intrisics.
        
         | jerf wrote:
         | I'm sure someone will be along to provide a wrapper macro or
         | macros to address that shortly.
         | 
         | I can see why they would not want to bless a particular syntax
         | tree or whatever into the core compiler.
        
           | gpm wrote:
           | (Once this is stable) haven't they blessed a particular
           | syntax tree anyways, since the contents of the "" string is
           | parsed to a specific syntax tree that will have to remain
           | stable. The only difference is that that syntax tree is
           | delimited "<contents>" instead of asm!(<contents>).
        
         | Someone1234 wrote:
         | If you want the outer language to be cross platform (e.g. x64,
         | ARM64, etc), then you'd either need a parser for every
         | potential platform (even ones you aren't currently using!) OR
         | you just settle for strings.
         | 
         | Don't get me wrong: Parsed assembly is superior because you can
         | have certain parser guarantees/checks. But it will definitely
         | limit the scope of your language or at least slow its adoption
         | to new platforms.
        
           | pjmlp wrote:
           | Productivity of language users vs compiler developers.
        
             | Someone1234 wrote:
             | Productivity of a niche area.
             | 
             | But related: There's no reason parsers cannot be
             | externalized and inject the resultant assembly strings into
             | the Rust solution. If that's really what people want. The
             | nice thing about external parsers is that they become
             | optional in every sense.
             | 
             | If the Rust team builds them in, then every supported
             | platform needs support, every development environment needs
             | every parser, and tooling also has to support it (e.g.
             | IDEs, code checkers, etc).
             | 
             | It isn't simply just adding it once and leaving it forever,
             | the work is repeated several times over and the maintenance
             | will be time-consuming for what is: A niche feature.
        
               | pjmlp wrote:
               | Better not starting talking about niche in regards to
               | Rust, unless adoption of a niche systems language isn't
               | something to care about.
               | 
               | Productivity matters everywhere, systems languages that
               | care about happy programmers have better chances of long
               | term adoption.
        
               | yazaddaruvala wrote:
               | It seems reasonable, but we need to not conflate niche
               | with lacking-popularity (i.e. low usage).
               | 
               | Niche is about the environment, i.e. the problem domain.
               | 
               | Analogy to biology/ecology, grazing on mountain tops is a
               | niche that is also unpopular (i.e. low usage), meanwhile
               | pollination is a niche that is popular.
               | 
               | The problem domain for Rust to be useful is very large,
               | across the programming ecosystem and across the
               | programming ecosystem effort is put into increase
               | accessibility for the less and less experienced (e.g.
               | embedded, wasm, webservers, clis, etc). However, Rust
               | _might_ currently lack popularity (i.e. low usage).
               | 
               | The problem domain for Inline Asm to be useful is very
               | small, typically by experts in narrow domains and across
               | the programming ecosystem effort is put into decrease the
               | need for inline asm (e.g. crypto, firmware, drivers,
               | etc). It also happens to be unpopular, and is avoided as
               | much as possible.
               | 
               | Summary, Rust is pragmatically niche, rather than
               | theoretically niche, Inline Asm is theoretically niche,
               | and also pragmatically niche.
        
               | Someone1234 wrote:
               | That's a pretty shallow argument, one that can literally
               | be re-used verbatim to argue for any and every
               | feature/philosophy. Particularly when "happy" is as
               | subjective as this.
               | 
               | It also ignores the reality that the Rust team's time is
               | finite, and if they invest it into three or more assembly
               | parsers then they aren't investing it into other areas of
               | the language that could _also_ be productivity
               | multipliers. Areas that more programmers actually use.
               | 
               | Ultimately how much is unsafe assembly in Rust going to
               | be used? And how large are those code sections? And are
               | you better off creating Assembly in existing dedicated
               | tooling and importing it, even if Rust had native parsers
               | (e.g. due to better debugging/visualization elsewhere)?
               | 
               | Honestly if Rust code outside of standard libraries ends
               | up with a ton of assembly anyway then that's a weakness
               | of Rust. You should be using as little as possible. Even
               | unsafe Rust is superior to assembly because it is
               | platform agnostic. I'd go as far as to call it a "last
               | resort."
        
             | alharith wrote:
             | There are infinitely more language users. I'd rather throw
             | a bone to the compiler developers.
        
               | AnIdiotOnTheNet wrote:
               | Then that means there is infinitely more productivity to
               | be gained catering to the language users.
        
               | pjmlp wrote:
               | One more reason to actually care about the productivity
               | of language users.
               | 
               | They are free to go elsewhere more welcoming.
        
           | zamalek wrote:
           | I really don't like the strings (because Rust is so good at
           | avoiding things like this), but your explanation makes tons
           | of sense.
           | 
           | That being said, it wouldn't be hard to create an asm_x86!
           | macro that hides the string.
        
         | kzrdude wrote:
         | Strings can be parsed. The rust compiler parses format strings,
         | for example.
        
         | newacct583 wrote:
         | It's not "PC based" that's the magic you want, it's the
         | assumption of x86 (and, very recently, sorta, ARM, in a few
         | compilers).
         | 
         | Architectures are weird, assembly is crazy. There just isn't
         | going to be a single set of semantics around what you want.
         | 
         | This Rust thing isn't revolutionary, it's indeed just a
         | prettier way of writing the model GCC invented a few decades
         | back:
         | 
         | + "Assembly" is a black box for the code author.
         | 
         | + The compiler's job is generating code _around_ the assembly,
         | not the code itself.
         | 
         | + So the fundamental model is "constraints": the rules the
         | compiler needs to follow before and after the assembly, and how
         | to emit an appropriate string to represent the
         | register/address/immediate/whatever requested by the assembly
         | code.
         | 
         | And like it or not, this works. It works really well.
         | Generations of OSes have been written in this model without
         | much complaint (beyond the general ugliness of the gcc syntax,
         | which Rust is trying to fix).
         | 
         | I think this is fine. I agree it's not going to bring a
         | revolution, but we really don't want one in this space.
        
           | pjmlp wrote:
           | GCC did not invent anything, that dumb approach to inline
           | Assembly is traditional in UNIX environments.
           | 
           | Amiga and Atari also had similar inline Assembly support.
        
             | not2b wrote:
             | GCC doesn't implement the "dumb approach" that existed
             | before it, and that Amiga and Atari originally had. The GCC
             | approach provides enough information to the compiler that
             | code can be optimized in the presence of inline assembly;
             | the compiler is informed of what the assembler code reads
             | and writes, and can allocate registers for it. It was much
             | more flexible than the implementation in the original Unix
             | compiler.
        
             | newacct583 wrote:
             | I still think you're missing the point. GCC indeed didn't
             | invent "inline assembly as a string", no.
             | 
             | GCC invented "constraint metalanguage as the foundation for
             | inline assembly generation", which is the model here that
             | enables one compiler and one syntax to work the same way
             | for basically every architecture over decades. Modelling
             | the interior of the assembly code as a black box (which in
             | this implementation means "string") is just a side effect
             | of this important design choice.
             | 
             | Again, the stuff you're imagining isn't a general purpose
             | assembly language model, it's an _x86-specific hack_ that
             | works only on the specific compilers and architectures for
             | which it was targeted.
        
               | pjmlp wrote:
               | Except that compilers with inline Assembly also don't
               | have dumb backends that just dump Assembly for as, like
               | on UNIX.
               | 
               | They interact with the backend.
               | 
               | They weren't specific of x86, although I mentioned only
               | PC, they were available across all 8 and 16 bit home
               | computers and several embedded targets support it as
               | well.
        
         | patrec wrote:
         | Are you aware of PeachPy? It's the only (Intel) assembler I
         | have ever seen I liked.
        
         | jcranmer wrote:
         | Microsoft declined to extend its inline assembly syntax to
         | support x64 or ARM-based processors.
         | 
         | If you want to use a single specific instruction or two, it's
         | usually better to just use the compiler's intrinsics (Rust has
         | already stabilized the x86 SSE and AVX intrinsics) than inline
         | assembly. When you go to more complex assembly fragments, you
         | tend to need more control over how the inline assembly fragment
         | interacts with scheduling and the register allocator, and
         | making that control work ergonomically is that much more
         | challenging.
        
           | [deleted]
        
       ___________________________________________________________________
       (page generated 2020-06-09 23:00 UTC)