1

It seem to be well known that x86 registers names have a purpose and indicate on how the register should be use (see this website for example).

According to this, ecx should be the register holding my i variable on the code bellow :

int main()
{
    register int i = 0;
    for(i = 0 ; i <= 10 ; i++){}
    return 0;
}

Objdump disassemble:


0000000000001138 <main>:
    1138:       f3 0f 1e fa             endbr64
    113c:       55                      push   rbp
    113d:       48 89 e5                mov    rbp,rsp
    1140:       53                      push   rbx
    1141:       bb 00 00 00 00          mov    ebx,0x0
    1146:       f3 0f 1e fa             endbr64
    114a:       bb 00 00 00 00          mov    ebx,0x0
    114f:       eb 03                   jmp    1154 <main+0x1c>
    1151:       83 c3 01                add    ebx,0x1
    1154:       83 fb 0a                cmp    ebx,0xa
    1157:       7e f8                   jle    1151 <main+0x19>
    1159:       b8 00 00 00 00          mov    eax,0x0
    115e:       5b                      pop    rbx
    115f:       5d                      pop    rbp
    1160:       c3                      ret

We clearly see that ebx is holding i, not ecx. Is there an historical reason to this? Did compiler used theoretical purpose or registers back then or was it just for humans?

2
  • Those are mostly just recommendations - ebx and ecx aren't different by any means and are "general purpose"
    – sudhackar
    Commented Apr 18, 2022 at 8:14
  • There's still the REP string operations that assume specific registers, e.g. REP MOVSQ copies ECX 64-bit words from ESI to EDI. If you're using those then you have to use ECX as the counter. Otherwise they're all broadly general purpose, yes.
    – Rup
    Commented Apr 19, 2022 at 7:07

1 Answer 1

4

The "intended purpose" of a processor register is generally irrelevant to a compiler, unless something fundamental about the instruction set or architecture makes use of that aspect of the register. For example, on x86, the esp register is implicitly used by instructions such as push, pop, call, and ret. As a result, the esp register cannot be repurposed as a general-purpose register. Similarly, some other instructions use fixed registers: there is a shl eax, cl instruction, but no shl eax, bl instruction; rep movsb moves ecx bytes from esi to edi; and there are other examples like this. ebp is technically only tied to its role as the frame pointer through instructions like enter and leave, so it can be used as a general purpose register, although it is often still used as a frame pointer to make debugging easier.

Outside of examples like this, where the compiler has no choice but to obey the intended purposes of registers in order interoperate with the architecture and broader platform, the compiler has no reason to care what somebody wrote in a manual 40 years ago recommending that eax be used as an accumulator and ecx be used as a counting register. Not only does the compiler have no sound way of determining in general which category a variable in the source code belongs to, no benefit would be obtained by putting variables into their "proper" register. In fact, having those unnecessary extra constraints would only serve to burden the compiler's job of producing performant code by making register allocation more difficult. Almost any register can be used for almost any purpose, and the compiler will exploit this in order to produce better code, which is a good thing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.