sandpile.org
Also, nop == xchg acc,acc
81–90 of 198 posts
sandpile.org
Also, nop == xchg acc,acc
Earlier quoted context omitted.
There are also AFIAK a few "deprecated" instructions that are implemented for backward compatibility but do not perform well on modern cores or have much better modern alternatives. These would be things like old MMX instructions, cruft left over from the 16-bit DOS days, etc. X86 is crufty. Of course all old architectures are crufty, and using microcode it's probably possible to keep the cruft from taking up much si…
Since P6, Intel's CPUs have used a RISC like core with a very heavy decoder that translates x86 CISC instructions to run on the internal ISA. With that in mind, do older or lesser used instructions actually perform poorly or are they just the wrong choice but actually preferred for other scenarios?
So, yes, there's a performance gap between instructions with hardware fast path and ones which require a microcode assist.
I don't know of any that use the BCD instructions like AAA. Other instructions have been essentially obsoleted like STOSB, etc.
And therein lies the rub. What is the minimum number of instructions a compiler could make use of to get everything done that it needs? I came across an article that says 'mov is turing complete' [1]. But they had to do some convoluted tricks to use mov for all purposes. I think it's safe to say that about 5-7 instructions are all that's needed to perform all computation tasks. But then: - Why do compilers not strive…
* x87 floating point is generally unused (if you have SSE2, which is guaranteed for x86-64)
* BCD/ASCII instructions
* BTC/BTS/related instructions. These are basically a & (1 * MMX instructions are obsoleted by SSE
* There's some legacy cruft (e.g., segment management) that's generally unused by anyone not in 16-bit mode.
* There are few odd instructions that are basically no-ops (LFENCE, branch predictor hints)
* Several instructions are used in hand-written assembly, but won't be emitted by a compiler except perhaps by intrinsics. The AES/SHA1 instructions, system-level instructions, and several vector instructions fall into this category.
* Compilers usually target relatively old instruction sets, so while they can emit vector instructions for AVX or AVX2, most shipped binaries won't by default. When you see people list minimum processor versions, what they're really listing is which minimum instruction set is being targeted (largely boiling down to "do we require SSE, SSE2, SSE3, SSSE3, SSE4.1, or SSE4.2?").
As for how many x86 instructions, there are 981 unique mnemonics and 3,684 variants (per https://stefanheule.com/papers/pldi16-strata.pdf). Note that some mnemonics mask several instructions--mov is particularly bad about that. I don't know if those counts are considered only up to AVX-2 or if they extend to the AVX-512 instruction set as well.
There are instructions that would almost never be useful. See Linus's rant on cmov http://yarchive.net/comp/linux/cmov.html The tl;dr is that it would only be useful if you are trying to optimize the size of a binary.
I didn't read Linus's rant on CMOV, but whenever you see a CPU with CMOV, it is because the hardware has very good branch prediction, and the compiler has intimate knowledge of how the branch prediction hardware works. Then the compiler works hard on determining if branches are highly predictable. Is the branch part of closing a loop? Predict that you will stay in the loop. Is the branch checking for an exception con…
And therein lies the rub. What is the minimum number of instructions a compiler could make use of to get everything done that it needs? I came across an article that says 'mov is turing complete' [1]. But they had to do some convoluted tricks to use mov for all purposes. I think it's safe to say that about 5-7 instructions are all that's needed to perform all computation tasks. But then: - Why do compilers not strive…
> - Why do compilers not strive to simplify their code-gen phase, or enable themselves to do advanced instruction-level program analysis, or both?
Because specialized instructions formalize invariants and constraints on behavior that allow efficient computation, often by specialized hardware.
> - Why do microprocessors not strive for simplicity, implement only a handful of instructions in an optimized way, with a very small chip footprint, to be followed by proliferation of cores (think 256-core, 512-core, 1024-core).
Some do, see GPUs and coprocessors like the Phi. We don't take this approach with CPUs because real problems often require complex, branching, inhomogeneous computation, which require the type of specialization and tradeoffs mentioned above.
My question is if compilers use "new" x86 instructions, as then the program won't work at all on old systems. For example, if Intel decided today that CPUs need a new "fast" hashing opcode (I don't know if they actually do), a compiler can't compiles to it, as programs won't work on older computers. Is it like the API cruft in Android, where "new" Lollipop APIs are introduced for 10 years from now, when no one uses a…
There are some methods to get around this. For example, there's an ELF extension called STT_GNU_IFUNC. It allows a symbol to be resolved at load time using a custom resolver function. This avoids the problem of figuring out which code-path to use on every invocation. For example, you could have a function void hash(char *out, const char *in); with two different possible implementations: a slow one using common instru…
Normally, asm would do
call slow_hash
at every place where slow_hash is invoked, but now it has to check at every invocation a pointer with the address of the function.Of course the loader could walk through all uses of the pointer to slow_hash and replace them by fast_hash on loading, but that won't work for selfmodifying (packed, or RE-protected) code.
Earlier quoted context omitted.
Now I'm curious - do you have performance numbers somewhere for this? The rep instructions can actually be shorter than a call to str*, so if rep is actually fast enough then it might make a nice optimization.
Reasonably modern versions of GCC will emit various rep instructions in some cases. Some code I just compiled with GCC 6.1.1 had several snippets like this emitted for zeroing with memset: xor eax,eax ... rep stos QWORD PTR es:[rdi],rax and some rep movs for memcpy/memmove.
Earlier quoted context omitted.
There are some methods to get around this. For example, there's an ELF extension called STT_GNU_IFUNC. It allows a symbol to be resolved at load time using a custom resolver function. This avoids the problem of figuring out which code-path to use on every invocation. For example, you could have a function void hash(char *out, const char *in); with two different possible implementations: a slow one using common instru…
I'm a bit skeptical about the performance, especially with often-called functions. Normally, asm would do call slow_hash at every place where slow_hash is invoked, but now it has to check at every invocation a pointer with the address of the function. Of course the loader could walk through all uses of the pointer to slow_hash and replace them by fast_hash on loading, but that won't work for selfmodifying (packed, or…
call's jxx's are expensive, hense CMOV ^_^