As someone who has both written and read a lot of assembly in a professional context, my colleagues who are assembly writers very frequently prefer Intel syntax. The ones who are mostly (exclusively) readers seem to generally prefer AT&T. It is pretty undisputed that AT&T is easier for computers to parse, but it's also generally easier for humans to parse when reading it.
* The suffixes give you a lot of easy specificity about what the operands are, while they are unnecessary if you already know what the operands are.
* DWORD PTR [x + A + B * C] is wordy, but at least the math is clear, and you can write the arithmetic in any order you want (as well as using 3, 5, and 9 as multipliers). X(A, B, C) is more concise and has no operand order to think about, and not terribly hard to read once you learn.
* AT&T syntax from a compiler always uses the easy cases and never makes mistakes about placement of the "glyphs" ($, %, etc.). There is none of the example's "movq ($28), %rax" because having a symbol called "$28" is fundamentally dumb.
* Operand order in AT&T is really dumb, especially for 3-operand instructions, but if you're just reading, you really don't need to know operand order to understand what's going on.
For these reasons, I assume the OP was written by someone who I assume writes a lot of assembly by comparison.
> As someone who has both written and read a lot of assembly in a professional context, my colleagues who are assembly writers very frequently prefer Intel syntax. The ones who are mostly (exclusively) readers seem to generally prefer AT&T. It is pretty undisputed that AT&T is easier for computers to parse, but it's also generally easier for humans to parse when reading it.
Wow, that surprises me. Without knowing anything, I would think it were the other way around, because in Intel syntax, the address calculation is more explicit than the comma-and-parentheses form; the registers don't require a % sign every time, making it distracting to read the code; and the opcodes are easier to read out loud because they lack the suffixes.
I suspect it's just a different culture. People coming from UNIX and C learn from tutorials that use AT&T syntax, and probably would never think of writing code in assembly to begin with.
If you write for any CPU other than an x86, the Intel assembler syntax matches the syntax order you would get. It also sort of lines up with C and C++ syntax of putting the result on the left.
I might be biased as an Intel-syntax-loving assembly-writing heathen.
> It also sort of lines up with C and C++ syntax of putting the result on the left.
The article also discussed this argument, and I find it weird, because higher level languages also put the operation on the right side.
Maybe I just prefer having the operation and the target close together?
Having `abs(x) = y` put the absolute value of y into x, would be extremly weird to me in C++.
> I might be biased as an Intel-syntax-loving assembly-writing heathen.
I guess the world is on your side, and I just avoid assembly where possible :D (although, not really because of the syntax part, I can live with either syntax decision in the end after getting used to it)
Careful, in C++ `abs(x) = y` is equally likely to put y into abs(x).
Jokes about C++ aside, many DSPs with their own assembly languages have chosen to learn from C and higher level languages. The following style of syntax is not uncommon for DSPs (I am not using a specific one, but an abstract style), where writing assembly is expected:
R1 = R2 + R3;
R5 = R1 * R3;
R4 = POPCOUNT(R1);
R3 = [R0] <- loads data at pointer in R0 into R3
Curiously, RISC-V didn't do this despite having the option to write their own new assembler. I guess the old opcodes die hard, but I also assume it has to do with LLVM strongly suggesting [OPCODE] [ARGS] syntax for assembly.
> Jokes about C++ aside, many DSPs with their own assembly languages have chosen to learn from C and higher level languages. The following style of syntax is not uncommon for DSPs (I am not using a specific one, but an abstract style), where writing assembly is expected:
> R1 = R2 + R3 R5 = R1 * R3 R4 = POPCOUNT(R1) R3 = [R0] <- loads data at pointer in R0 into R3
That's really cool to know, my exposure to assembly is mostly limited to a little bit disassembly for all kinds of reasons (so mostly x86 reading) or 1 instance of inline assembly. Most of the other instances I worked with SSE intrinsics instead, so my knowledge is very limited.
I first learned assembler on Z80, and so the Intel syntax should be more natural to me, but I migrated from that to 68000 which uses destination-last syntax, and never really had any problems with it even though I'd been using Z80 for a few years by then. About a year after learning 68000, I started coding 8086 assembler and felt that Intel syntax was backwards, even though it's the same order as the Z80 I started on. Now it's been almost 2 decades since I wrote 68000 code, and I've drifted back to Intel syntax as the default, although I've always had a particular hatred for the square brackets for memory accesses. I remember being particularly disgusted by the DWORD PTR nonsense that the original MASM insisted on, and back in the day preferred a86 (no, not the Linux one, the old shareware DOS assembler from the late 80s) which had a much more sane syntax.
AT&T syntax also pre-dated Intel syntax by several years, and was based on the PDP-11 assembler which had the destination last. Many UNIX vendors later moved to 68000 which also had the destination last (possibly influenced by AT&T syntax, as all their 8-bit processors embedded the destination in the opcode). Other chips like SPARC, which was developed by a UNIX vendor for a UNIX-like machines followed suit, probably because all their engineers were already using AT&T syntax anyway.
As for the other arguments in the article, I think it's kind of fair to complain about the syntax, but also important to remember that as part of the GCC toolchain, the assembler was never really intended for humans to use, other than for short bits of glue code, but was instead really intended to just have enough functionality to assemble the output from cc1. When GCC was ported to Intel, it almost certainly made sense to keep AT&T syntax in gas because gcc developers were familiar with it, and the cc1 backend would probably have required extensive changes to output in a different order even though it wouldn't really benefit anyone because nobody was actually intended to read the intermediate .S file other than while debugging gcc itself. Obviously nowadays, cc1 directly outputs to .o and the -S flag is more for debug, and sometimes generates code that doesn't always exactly correspond to the generated code in the .o.
But anyway, despite all that defence for why AT&T syntax, I agree, it's ugly and I always hate writing code with it. I almost always use an assembler that uses the chip vendor's preferred syntax on whatever platform I'm using, mostly because it's then easier to refer to documentation.
If Intel allowed you to do "MOV RAX, [address]" (inferring the QWORD PTR part from the "RAX"), most peoples' primary aesthetic objection to the syntax would be gone.
Which assembler requires that? Let me guess, GAS in Intel mode, just to make it more painful because they don't want you to use it?
NASM syntax:
mov rax,[Foo] ;load var into reg, QWORD is implicit
add dword [Bar],1234h ;add constant to DWORD var
movzx eax,byte [rsi] ;zero extend BYTE to DWORD reg
MASM was also excessively wordy in many other areas. The amount you had to write to introduce a new segment or access something from another segment was ridiculous. I guess they probably improved that in later releases after TASM showed you didn't need any of that nonsense (and just had .TEXT and .DATA instead).
How many of them have experience with other assembly languages?
For me, it was exposure to M68k that made Intel syntax seem absolutely awful. AT&T syntax is also awful, but at least the operand order is the way that I'm (still) more used to.
Many of the assembly writers have exposure across platforms, but often with modern assemblers whose syntax tracks Intel syntax more closely.
I don't know why 68000 is so popular here (maybe people on HN learned it in school?), but you're far more likely to be writing ARM assembly or these days RISC-V assembly than writing for an antiquated core like the 68000.
Believe it or not, outside the x86 world, Intel-style syntax has basically won.
* The suffixes give you a lot of easy specificity about what the operands are, while they are unnecessary if you already know what the operands are.
* DWORD PTR [x + A + B * C] is wordy, but at least the math is clear, and you can write the arithmetic in any order you want (as well as using 3, 5, and 9 as multipliers). X(A, B, C) is more concise and has no operand order to think about, and not terribly hard to read once you learn.
* AT&T syntax from a compiler always uses the easy cases and never makes mistakes about placement of the "glyphs" ($, %, etc.). There is none of the example's "movq ($28), %rax" because having a symbol called "$28" is fundamentally dumb.
* Operand order in AT&T is really dumb, especially for 3-operand instructions, but if you're just reading, you really don't need to know operand order to understand what's going on.
For these reasons, I assume the OP was written by someone who I assume writes a lot of assembly by comparison.