1. I don't think I am overselling rexcode, rather there isn't anything like it in the first place as a single library with that many ISAs and IRs. That's what I love about it, since I can now trivially make (non-optimizing) compilers without needing to use LLVM or use another external tool (including outputting C or assembly). Also all of those tables become binary blobs, so you could just use them in your favourite language any way.
2. That was a bug and it has now been fixed, along with many other analysis bugs.
3. The sorting algorithms are getting an overhauls soon to be a lot faster.
How is this not an assembler?—even with your description which matches an assembler to a tee. It genuinely is an assembler, and I am not sure how you are thinking otherwise.
And where is the AT&T? Did you even look at the syntax or read the article? Is it just the use of `%rex` to prevent namespace collisions with parameters and constants which could hypothetically be named `rex` (and there could be good reasons they are named that too)? There are no other sigils in the grammar. The order of the operands is Intel-like. The memory operand syntax is Intel-like.
Odin is never going to support SHARC nor Hexagon, so it is literally not a problem.
And I do not even seen why a universal syntax for such ISAs is impossible to support either at the syntax level. Hexagon's `.new`/`:sat`/`:<<1` stuff could be easily added into the universal syntax (with a better syntax), even if other ISAs do not support it. Same with SHARC's parallel-operation separators: you just pick a different syntax.
Even now, the full `[base + indexscale + disp]` syntax is not semantically supported for RISCV64 because they do not support `indexscale` in their memory operands.
Yes the prefix syntax is a quirk but if can tell me an alternative syntax that is context-free to solve this problem that is also not too stark nor dense too read, please do! I am open to new ideas, but it seems that even other assemblers like Plan9, Go, and D, all came to similar conclusion with `lock; xadd ...`.
> Why is a bad thing that another language design and compiler writer compares his language as a point of comparison?
"why is it a bad thing if you do X thing incessantly". in this specific case it's called shilling. if you've been on hn for longer than a week you'll notice he advertises D as if it's his fulltime job (which it probably is).
Google defines "shill" as: "A shill is a person who is paid or secretly works with a trickster, gambler, or salesperson to trick others into buying something or joining a game by pretending to be a normal, happy customer"
I am not paid, nor work secretly (I use my real name), and am not tricking anyone.
D's approach is great, but it has a few limitations for my use case in Odin. It only supports x86_64/amd64 and uses Intel-style syntax, whereas I needed a solution that universalizes its syntax across multiple ISAs.
D's inline asm is also statement-based rather than a callable template. Though the mixin trick fixes this, it does mean it still uses %0-style parameters making it hard to read and write, something I want to remove completely.
It is great to see that we arrived at similar design compromises, especially regarding `lock` being treated as a separate instruction and thus separated with a `;` (which is automatically inserted by the Odin compiler).
Yes, because the instruction set references are in Intel syntax. The backwards gcc asm causes me seizures, like trying to write cursive with my left hand. The asm for Arm64 will also follow Arm's instruction specification.
> D's inline asm is also statement-based
That's so the source code can be tokenized and parsed without needing special behavior inside the asm { ... }.
> it still uses %0-style parameters
Not sure what you mean. RAX means register RAX. %RAX is not accepted.
> especially regarding `lock` being treated as a separate instruction
That just makes it easier to parse!
Anyhow, thank you for the kind words! I am proud of it, the only troubles I have is when Intel adds wacky new instructions that just don't fit in the instruction encoding tables.
Well I assumed so because amd64 is a superset of x86. But nice to know you're working on arm64 too.
Regarding Intel-syntax, I think there is a little miscommunication here since I try to explain what I mean in the article. Intel-ordering is a good idea, but using nothing but the Intel-syntax wholesale is not universal enough, and needs modifying, especially for AMD64 and other ISAs. Odin's is Intel-like too, but fully Intel by design.
> That's so the source code can be tokenized and parsed without needing special behavior inside the asm { ... }.
This is why Odin's asm templates have their own universalized syntax. Thus the entire article.
> Not sure what you mean. RAX means register RAX. %RAX is not accepted.
It's why I referred to your "trick", which is something I wanted to need in the first place.
> That just makes it easier to parse!
For Odin's asm template syntax, it's not about being easier to parser, it's about having a context free grammar that is the same across ISAs. If I was to allow for prefixes directly in the grammar, either prefixes would have to have their special syntax or you'd need to have a context-sensitive grammar.
Oh I see what you mean. The "trick" has nothing to do with the inline assembler - it's a way the user can manipulate strings and then feed the result to the parser. String mixins are a very popular feature of D.
A little secret - the D parser does not actually parse the asm syntax. It just snarfs up tokens until it sees the `;`. The semantic phase of the compiler then applies a grammar over it, which is not the D grammar, but the Intel grammar. This enables it to apply custom grammars to each supported instruction set.
That "trick" is just a fancy string effectively, the thing I am complaining about in the first part of the article. And that little secret really does mean it is a fancy string again.
> the different operand sizes have a lot of side effects
Which we have massive tables for each form which track those side effects and clobbering information too.
> author/LLM
I am the author, and not an LLM.
> Tomorrow you need to pass a 128 bit int into two registers
Okay? There are no 128-bit integer registers on AMD64, ARM64, nor RISCV-64. So I have no idea what you are on about. And note they are templates, so if you want 128-bit integer support, you can just wrap that template in a procedure and handle the behaviour yourself.
Have extremely different performance characteristics, yet would map to the same code:
ld1d dst, p0/z, [base + idx<<3]
Imo this makes reading the assembly quite bothersome. I'm already not a fan of ARM64 doing the mnemonic overloading, but at least you can figure out the operation by looking at the same line further to the right.
Also, maybe I missed it, but how are you dealing with things like the /z modifier, pre/post-increment load/store and load pair? Or things like TBL/ST4/LD4?
Oh and how are the types going to work for RVV, where the type can't be determined at compile-time in all situations?
So the parameter is marked as a predicate with zeroing or whatever, and then `pred` is just a normal operand as the binding section specifies everything.
This is not current behaviour yet but it I am considering it when I need to specify this for even AVX-512 and RISC-V behaviour (which has multiple different possibilities).
>Which we have massive tables for each form which track those side effects and clobbering information too.
No, you have tables _of the instructions that the compiler codegen may use_. You have no tables of what someone may use inside inline assembly, because for most architectures it may not even be possible to build such tables in the first place!. That's a reason why usually you rely on the users specifying the side effects manually for these cases.
> Okay? There are no 128-bit integer registers on AMD64, ARM64, nor RISCV-64. So I have no idea what you are on about.
You have no idea why you would need to pass a 128 bit int in two registers if there are no two 128-bit integer registers? Am I missing something here?
Even rdtsc is already returning a 64-bit into two registers (another x86 idiosyncrasy I suppose), rather than "two separate return values", something the examples kind of gloss over.
> No, you have tables _of the instructions that the compiler codegen may use_.
That's a distinction without a difference.
> ...because for most architectures it may not even be possible to build such tables in the first place!.
Name the architectures and the specific instructions; do not be hypothetical. In certain runtime-dependent cases like AVX-512, the clobbering is runtime-dependent which then can be explicitly stated by the user.
> You have no idea why you would need to pass a 128 bit int in two registers if there are no two 128-bit integer registers?
I completely understand, my point is that you would pass the two 64-bit parts into separate registers. There are no 128-bit integer registers on the platforms we care about, but if they did exist, supporting them would be trivial. So if you want to pass an 128-bit integer, it will have to be done in two registers, which is literally the point. `asm` templates are not necessarily meant to be used bare all the time, but sometimes it is better to wrap them in a procedure with the correct calling convention too (e.g. "c" or even "naked") if you want to utilizes Odin's native 128-bit integer types as part of the parameters.
> Name the architectures and the specific instructions; do not be hypothetical.
You realize you're asking for a list of instructions that are not used by codegen but exist in the ISA? Because it is practically infinite.
Even a plain old "in" in x86 may go from clobbering only the target registers to clobbering memory to clobbering about _every_ register (e.g. under vmware). And there's a million like these on any architecture.
Short of generically saying all clobber everything, I really don't know how can you build a table here.
And you are forgetting that the problem does not only extend to the compiler here, but to whoever is writing the assembly, because you may be using some register that may or may not be clobbered on depending on which 'mul' instruction operand size was used by the previous one!
> So if you want to pass an 128-bit integer, it will have to be done in two registers, which is literally the point
Some inline assembly syntax (e.g. Watcom) does support return an int64 as 2 registers. Are you understanding this as me asking to change the instruction to return the value in one register or something? What I'm saying is that it supports mapping its 64 bit native type (which is either two registers or always in memory, I don't care) to an inline assembly snippet that uses/returns an int64 value in two registers.
This is just an example of the reason inline assembly syntax grows unwieldy, and there are more! Just about any 'letter' of gcc's extended ASM is another one.
You seem to be trying to implement something like intrinsics here, with a very limited view of what people use inline assembly for, and that's fine, but it simply falls short, and that is why you have a hard time explaining the decisions behind inline asm syntax.
The parent was objecting to the syntax allegedly forcing one to use “positional references into a list you have to count by hand”. Being inaccurate in your criticism just makes it appear questionable as a whole.
The issue is you're usually using inline asm for special cases, or else CPU features so new the compiler doesn't know about them. Which means it also doesn't know the clobbering rules.
That's why it looks like an escape hatch. It is one!
The technique is good, and compilers that interact with assembly should do this, but as you outline, they basically just shove blobs of text around and hope for the best.
I didn't say you were referring to TALs. Yours is a syntax level check, not type checking of the program in the normative sense. It might be more accurate refer to your technique as an "instruction signature", rather than a type.
I would argue that that are complementary and not entirely different.
But a language being “typed” doesn’t tell us anything useful. Untyped languages are typed too: they’re uni-typed (every expression is an expression).
I think you do your analysis a disservice by focusing on “is assembly language typed?” as the top line question. The more interesting question you examine is what do the type constraints in inline asm offer, and how do they interact with the host language’s type system?
I know that "untyped" means a single-type, but assembly operands have multiple different kinds of types (as I state in the article). What makes it really interesting is what you can know about each instruction and what it does (what operands it excepts, what it clobbers, what side-effects its has, etc).
And from that huge table of type information, this can be used to give good error messages and suggestions to the user because the compiler actually knows all of this. The type constraints here allow for a lot more than information that normal assemblers just don't give.
In other words, the type of an assembly instruction specifies its effects and coeffects. This is an active research area—describing effects and coeffects in the type system, and discharging handler/provider obligations at the compiler level.
I suspect the main reason someone might quibble over the “assembly is typed” assertion is that many programmers have a rather narrow view of type systems, heavily skewed by OOP patterns.
That's pretty much the quibble. Most people's narrow view of type system.
And we already track all of the basic side-effects and clobbering that each form of each mnemonic does. That's kind of the entire point of this being possible: it's all "typed".
Vim does not have multiple cursors which can be placed ANYWHERE even multiple on the same line and at any offset. You are clearly not aware of what Vim actually offers.
To be fair to the commenter, you didn't provide any details in the example you provided. For all we (the readers) know, Visual Block mode would have worked in the situation you are describing. I have to assume not, but it's not clear from the article.
Vim macros are not that hard to write, but I do agree with you, visual feedback is better. It can be annoying to have to re-record a macro, that's why I tend to use :s (search & replace) or Visual Block mode over using a macro (most of the time I don't need a macro).
I understand you state vim is "just an example", but you use this example as the main backbone of your article. Add more detail. You're arguing with vague anecdotes which requires the reader to read between the lines.
An aside: visual feedback, multi-cursor support and sane defaults is likely what led to new modal editors being created, such as Helix and Kakoune.
Same line edits can be done with search and replace and regex. And your article doesnt specify offsets, in which case you can use a plugin, which you state you already have to do that for sublime's flaws. There's so many other examples you couldve used.
2. That was a bug and it has now been fixed, along with many other analysis bugs.
3. The sorting algorithms are getting an overhauls soon to be a lot faster.