I like your outlines so far. It is very interesting -- even if it probably will fail eventually.
I am only concerned with this quoted guideline. Performance should always be a priority. Your other guidelines -- keeping the translation simple and transparent -- already put a constraint on the aesthetic. You can't allure users by hiding complexity beneath something. So if you also don't promise users performance, what good is your language to a user (unless he is a poet)?
I feel that you are adding this guideline only because you are thinking about taking the dynamic approach to memory safety rather than the Rust approach. That is fine as long as either approach is only an add-on rather than the permanent infrastructure of your language. If they are, then I am afraid your language will just reduce back to one of the language that is hardly worth to checkout.
My suggestion is always to put performance as your priority. Always start with solutions that performs, then work on enhancing your language to express the same solution easier.
Also, the word "priority" means relative importance. Too many priorities are equivalent to no priorities. Performance not being a priority just means other aspects are more important, not that performance will be glacial. Indeed, a lot of performance stems from doing less, and I fully expect fewer layers of abstraction to keep Mu quite competitive for many tasks. But that will happen naturally without me needing to push on it.
Reminds me a bit of LLVM-IR.
If you do this in C, most compilers will optimize into a 64-bit rotate:
(n << d)|(n >> (64 - d))
Many compiler vendors have an extension that will compile to a rotate instruction, but they tend to differ from one compiler to another.
1. Mu isn't intended to be portable. There are no other backends, not even other processor families. Though you still have to put up with other processors within the same family, if it's a new instruction that older processors don't have. So far it just focuses on lowest-common-denominator 32-bit instructions.
2. The whole goal with Mu is to eliminate impedance mismatch with the architecture. It's relatively straightforward to add another instruction at the Mu level called 'rotate', and have it code-generate the appropriate x86 instruction. That seems less convoluted than assuming a fixed language and compiler and expressing a rotation out of 4 binary operators.
3. Finally, there's also the option of writing some functions in the 'machine code' level called SubX. Functions generated by Mu and SubX will freely inter-operate, though SubX functions lack safety guarantees.
What you do give up if you use expression syntax is control over register allocation, unless you specify that expressions are evaluated via the stack and a designated accumulator. Or you could annotate every single subexpression with the register to put it in. I'd agree that this might not be a good fit for the overall design of this language, but I don't think CSE is to blame.
Besides that, the description of the calling convention could use a few more details. Are all registers except the stack pointer and the return value(s) callee-saved? Can you return multiple values? Can each function choose where to return its results, or is eax fixed as the return register? The "may be just documentation" comment confuses me.
Calling convention: yes, all registers are callee-saved. Multiple values can be returned. (Up to 6, I guess?) Since the callee saves registers, any returned registers are just not saved and restored. A function can return in any register, though I use `eax` by convention. Since miscommunication between caller and callee about what registers are clobbered can lead to subtle bugs, I require the caller to also show the correct register being written to. Does that help? I'll work this in. Thanks!
"Mu will be a manually register-allocated language. It will still check your register allocation and flag an error if multiple variables overlap on the same register. But it's up to the programmer to manage registers and spill to memory when necessary."
The goal is not a specific level, just to do as much as possible with local rewrite rules. That gets some surprisingly high-level features, but the result overall will still seem fairly low-level.
(And then there's all the runtime overheads. Mu isn't trying to compete on performance.)
So far I've made some progress with code-generation. Next I plan to switch gears and work on the parser. I try to post updates on Mastodon: https://mastodon.social/@akkartik/103052146965158614