4 ms·
This paragraph caught my attention This [variable-length coding] is a disadvantage for x86, yet it doesn’t really matter for high performance CPUs ... Why a
by koito17 2y ago
This paragraph caught my attention
This [variable-length coding] is a disadvantage for x86, yet it doesn’t really matter for high performance CPUs ...
Why are they leaving out difficilty of building a compiler backend when the ISA has variable-length codes? I would assume an ISA needs to consider its burden on compiler authors. (Itanium is an extreme example of an ISA too tedious for compiler authors)
I once extended a Common Lisp compiler to emit machine code for SSE4.2 instructions (specifically minss and maxss). The experience was a bit bad due to subtle differences in prefixes and specific fields needing to be set to activate some mode for SSE4.2 instructions.
Now suppose you want to debug a compiler backend targetting x86. Good luck, x86 disassembly is an undeciable problem because you don't know where the instructions start. Meanwhile ARM has two kinds of instructions (full-size and half-size, called "thumb"). Thanks to fixed-length instructions and alignment rules, you always know whether you're in thumb or not. Emitting machine code (and disassembly) is much more straightforward
- mhh__ 2y agoThe amount of code that's being run compiled with systems for which this is a pressing issue is probably ~0%. You're either using an established backend that does all this for you, or realistically you can throw some money at it and make it go away.
- tptacek 2y agoBecause they're not talking about software development cost in the first place.
- pizlonator 2y agoCompilers don’t have to disassemble instructions, so the fact that x86 is hard to disassemble doesn’t matter. And it’s not actually hard in practice if the binary contains labels for function starts. Neither x86 nor arm are easier to compile for, because they both have different quirks that cause headaches. X86 has two operant forms, which require more work. Arm has restrictions on conditional jump distance and immediate encodings, which requires more work. So, an instruction set that had no two operant forms like x86 and that allows any instruction to have any sized immediate would probably be easiest to compile form. Allowing any instruction to have a 32-bit or even 64-bit immediate would lead to either all instructions being huge or to variable length instructions. X86’s use of variable length instructions allows 32-bit immediates everywhere, so in that sense, it makes compiling easier.
- drivebycomment 2y agoThe variable-length instructions make very little difference in building a compiler at this point, that it's practically negligible. If you look at GCC or LLVM, while there is some extra code to deal with the variable length encoding, it's not anywhere near big enough of a challenge for them. > I once extended a Common Lisp compiler to emit machine code for SSE4.2 instructions (specifically minss and maxss). The experience was a bit bad due to subtle differences in prefixes and specific fields needing to be set to activate some mode for SSE4.2 instructions. I assume this was some toy compiler or a non-optimizing compiler. LLVM or GCC (or any other industrial strength optimizing compilers) have no trouble whatsoever dealing with any of those. The difficulty with more complex instructions like vector instructions is in optimization / being able to find the code pattern that can take advantage of the complex instructions, and that has nothing whatsoever to do with them being variable length encoding or prefixes or knowledge about instruction set themselves. If the program is already written for it - e.g. using intrinsics - emitting and mapping to the machine code is trivial, regardless of how complex the instruction encoding rule is.
- koito17 2y ago> I assume this was some toy compiler I dont know the definition of "toy compiler", but compare the following (x86 backend vs arm64 backend) https://github.com/Clozure/ccl/blob/d960a0e/compiler/X86/x86-asm.lisp#L547 https://github.com/Clozure/ccl/blob/d960a0e/compiler/X86/x86... https://github.com/Clozure/ccl/blob/d960a0e/compiler/ARM64/arm64-asm.lisp#L294 https://github.com/Clozure/ccl/blob/d960a0e/compiler/ARM64/a... I would argue the former is a lot more complex compared to the functional equivalent in arm64 The specific extension to allow e.g. minss would look something like this (def-x86-opcode minss ((:regxmm :insert-xmm-rm) (:regxmm :insert-xmm-reg)) #x0f5d #o300 #x0 #xf3) Now try doing the same for e.g. movsxd, and you will have to be careful with the ModR/M byte, due to the VEX prefix changing semantics.
- jcranmer 2y ago> Now suppose you want to debug a compiler backend targetting x86. Good luck, x86 disassembly is an undeciable problem because you don't know where the instructions start. Meanwhile ARM has two kinds of instructions (full-size and half-size, called "thumb"). Thanks to fixed-length instructions and alignment rules, you always know whether you're in thumb or not. This juxtaposition is (I assume unintentionally) hilarious. ARM is the ISA where you might reasonably see two different ISAs (and thus a disassembler needs to handle both) in the same object file, where x86 only has one, at least since the mid-90's. Yet somehow x86 is undecidable and ARM is straightforward! In reality, the problem you're talking about for x86 is trivially solvable with what's known as recursive disassembly. You start disassembling from known function locations (since you mention debugging a compiler, that means your binary should be fully symbolized, but finding common function entry points for an unsymbolized binary isn't that difficult of a challenge), and then continue disassembling instructions until you get to various kinds of jump instructions. Then you add branch locations to the list, and rinse and repeat until you're out of new locations to disassemble.
- saagarjha 2y ago> Thanks to fixed-length instructions and alignment rules, you always know whether you're in thumb or not. As someone who did this literally yesterday, no you do not. Doing this properly requires tracing control flow, which is an undecidable problem. In fact x86-64 is way easier in practice because real code is somewhat self-synchronizing if you just disassemble linearly and for ARM this doesn’t work nearly as well.
- inkyoto 2y ago> Why are they leaving out difficilty of building a compiler backend when the ISA has variable-length codes? Because compiling into the maching code and the machine code decoding by the CPU have different time constraints. One can even argue that a compiler has an infinite time (however impractical it is) to translate a programming language into the binary code, whereas the CPU does not have the same luxury hence the haggle over the ISA's. So it makes sense to leave the compiler code generator out of the picture.