Live data from Hacker News

Does a compiler use all x86 instructions? (2010)

pepijndevos.nl

91–100 of 198 posts

Re: Does a compiler use all x86 instructions? (2010)

#91
post #68
post #2

It doesn't, because there are lots of special-purpose x86 instructions that would be more trouble than they're worth to teach a compiler about. For example, instructions for accessing particular CPU features that the C and C++ languages have no concept of (cryptographic acceleration and instructions used for OS kernel code spring to mind). Some of these the compiler might know about via intrinsic functions, but won't…

About LEA: adding to the above correct information, it is also useful because it can be scheduled in parallel with other ALU instructions (through a different "port" in x86 parlance), or at least that's how it used to be (I haven't looked at most recent x86 architectures). Thus compilers can use LEA to perform some arithmetic operations and generate code that will effectively run faster.

On recent Intel CPUs, LEA is generally executed with the same resources as other arithmetic.

LEAs touching 16-bit registers issue multiple micro-ops and are slow (the machine no longer has native 16-bit register support and has to mask the results in a separate op).

LEAs with 1 or 2 address components are issued to port 1 or 5 and have a latency of 1, and LEAs with 3 components or a RIP-relative address are issued to port 1 exclusively, and have a latency of 3.

In contrast, register-register/immediate adds and subs go to any of ports 0,1,5 or 6 and have a latency of 1, and register, immediate shifts go to ports 0 or 6 and have a latency of 1.

Converting operations to LEAs really doesn't buy as much today as it used to, but a smart programmer or a compiler can occasionally grab a cycle here or there.

Re: Does a compiler use all x86 instructions? (2010)

#92
> Note that the x86 was originally designed as a Pascal machine, which is why there are instructions to support nested functions (enter, leave), the pascal calling convention in which the callee pops a known number of arguments from the stack (ret K), bounds checking (bound), and so on. Many of these operations are now obsolete.

http://stackoverflow.com/questions/26323215/do-any-languages...

Re: Does a compiler use all x86 instructions? (2010)

#93
post #51

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…

[deleted]

Re: Does a compiler use all x86 instructions? (2010)

#94
post #78

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…

Sure, there is a lot of historical baggage in microprocessors--the BCD stuff and x86-16 support in general only exist for backwards compatibility (although note that BIOS starts up in x86-16). But the reason that Intel keeps adding instructions is, well, because they're useful.

> - 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).

What you're describing is a GPU--revert a core to a basic ALU and make it be 2 dozen 16-wide SIMD ALUs. Not everything can work well on a GPU, though; you need very high parallelizibility, and fairly uniform memory access models. It's why supercomputers switched from SIMD to MIMD models a few decades ago. (They're starting to revert to include more GPU-like cores, mostly for power reasons).

Re: Does a compiler use all x86 instructions? (2010)

#96
post #9
post #2

It doesn't, because there are lots of special-purpose x86 instructions that would be more trouble than they're worth to teach a compiler about. For example, instructions for accessing particular CPU features that the C and C++ languages have no concept of (cryptographic acceleration and instructions used for OS kernel code spring to mind). Some of these the compiler might know about via intrinsic functions, but won't…

lea is also used frequently to actually load addresses. When passing the address of a stack buffer, for example, the compiler will usually generate code like "lea eax, [ebp-0x80]; push eax". Or, when loading the address of a struct member, you might have "lea ecx, [eax+8]".

Yup, the original intention behind lea is the & operator. Compute the address but instead of accessing the memory immediately (like mov does) just keep it for later use. Just what & does.

Re: Does a compiler use all x86 instructions? (2010)

#97

In general: * 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 predicto…

> * There's some legacy cruft (e.g., segment management) that's generally unused by anyone not in 16-bit mode.

OpenBSD uses segments(while in protected mode!) to implement a line-in-the-sand W^X implementation on i386 systems that don't support anything better. The segment is set just high enough in a processes space to cover the text and libraries but leave the heap and stack unexecutable.

This mentions this implementation: http://www.tedunangst.com/flak/post/now-or-never-exec

Re: Does a compiler use all x86 instructions? (2010)

#98

The article assumes that no software in bin is written natively in asm or has asm blocks or linked objects... which seems a bit out there.

My thought was that most binary distributions probably use very conservative configuration that will generate code that compatible with very old processors, and that you would therefore not see much use of modern instructions in /bin. This is one of the selling points of compile-yourself distributions like Arch/Gentoo: you know what processor you're running on, so you can take full advantage of its features.

I'm running Ubuntu. Would be interesting to see output from a Gentoo user.

[edit] Gentoo: https://bpaste.net/raw/160c1fc57842 More Gentoo: https://paste.pound-python.org/show/BOVT5z1G93GRmGGfUyQQ/

Re: Does a compiler use all x86 instructions? (2010)

#99
post #78

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…

Convoluted tricks? Certainly. There's a functioning mov-only compiler [1], though. [1] https://github.com/xoreaxeaxeax/movfuscator

Re: Does a compiler use all x86 instructions? (2010)

#100
post #9
post #2

It doesn't, because there are lots of special-purpose x86 instructions that would be more trouble than they're worth to teach a compiler about. For example, instructions for accessing particular CPU features that the C and C++ languages have no concept of (cryptographic acceleration and instructions used for OS kernel code spring to mind). Some of these the compiler might know about via intrinsic functions, but won't…

lea is also used frequently to actually load addresses. When passing the address of a stack buffer, for example, the compiler will usually generate code like "lea eax, [ebp-0x80]; push eax". Or, when loading the address of a struct member, you might have "lea ecx, [eax+8]".

Loading an address is just adding a fixed number to the value in a register. Your two examples are mathematically equivalent to "eax = ebp - 0x80" and "ecx = eax + 8."
Post reply on HN