1

Will using a 32-bit register to store an integer that is equal to or less than 16 bits be inefficient, if the integer is always able to fit in a 16-bit register?

mov eax, 180 ; Is this overkill?

Also, if I am storing the same integer in a 16-bit register and it is expected to grow via user input into an integer greater than 16-bits, should I use the 16-bit register until it needs a 32-bit register or begin initially with the 32-bit register to save moving into it later on?

  • 3
    using 16-bit registers is actually the worst among the 8, 32 and 64-bit ones – phuclv Mar 30 '21 at 09:55
  • you only need to care about size when operating on a big array because that affects how many elements that can fit in a single cache line or a single SIMD register. In single values always prefer the native int size – phuclv Mar 30 '21 at 14:33

2 Answers2

2

Will using a 32-bit register to store an integer that is equal to or less than 16 bits be inefficient, if the integer is always able to fit in a 16-bit register?

Yes, it is a waste of processor resources, but they are there anyway and in many cases it is difficult to use the leftover register bits.  For example, while on x86 there is a register name/alias for the 1st & 2nd 8-bits of a 32-bit register (e.g. al & ah), there is no register name or alias for the upper 16-bits of a 32-bit register (just ax for the lower 16 & eax for the full 32), so those extra bits are very hard to use efficiently for some other purpose when the lower bits are otherwise in use.  (Consider also that often we don't use the full set of the registers at all, so that can be seen as waste as well — but this is not waste to be terribly concerned with.)

On other architectures, e.g. MIPS, RISC V, ARM, there are not even any aliases for 8- or 16-bit registers; they are all assumed to be (at least) 32-bits wide.  Most operations define how the full 32-bit registers are set, and broadly speaking, only small-sized stores to memory use partial register values.

@Arthur makes a good point about the encodings — they are actually ~optimized for the 32-bit data type on a 32-bit machine, and asking for only 16-bit operation sometimes requires an extra byte!  Most of this is only an issue on x86/x64 as those other processors don't even have the ability to specify 8- or 16-bit arithmetic at all.  x64 processors define operations on 32-bit registers as also having a specific effect (e.g. zeroing out) on the upper 32-bits of the 64-bit registers, so that even 32-bit operations update the entire register — this saves the merge operation that would otherwise have been necessary in doing 16-bit or 8-bit operations on the registers where the upper bits' prior values carry over instead of being definitely set by arithmetic.  (Again this is mostly an x86/x64 issue as other processors don't even attempt to make the small/smaller registers available to the programmer.)

should I use the 16-bit register until it needs a 32-bit register or begin initially with the 32-bit register to save moving into it later on

No, you'll find it extraordinarily difficult to write a program that uses 16-bit integers until it works with larger values and then switch to 32-bit.  You'll basically have to write your code several times over and with complex tests to decide when to switch.  (Further, what if you're doing addition, say, an only one of the numbers requires 32-bit but the other still fits in 16-bits — we might consider introducing yet a third or fourth case of the same code.)

Much more practical to ~upgrade to 32-bits in the first place, write your code once and omit any complicated switching from one to the other.  Easier to write, easier to test (and most likely more performant by omitting tests for which code path to use).

Btw, this is applicable to most any language, not just assembly language.

You can also use variable length integers, broadly speaking known as BigInteger, and these will automatically right-size, but using them has a significant performance cost for smaller numbers that could have been handled using the processor's natural sizes/data types.

Erik Eidt
  • 11,623
  • 1
  • 21
  • 39
  • Most 64-bit ISAs allow 32-bit operand-size for at least some instructions, with some semantics for what happens to upper bits. AArch64 is quite similar to the x86-64 model with `w0..31` ("word") being the 32-bit low halves of 64-bit `x0..31`, and writing a `w` register zero-extends into the `x` register. No partial-register shenanigans like x86 has for 8 and 16-bit though, except for ARM32's version of NEON SIMD where each `q` register aliases a pair of `d` registers. – Peter Cordes Mar 30 '21 at 12:54
0

I'm assuming you're using a 32 bit OS here. The extra 3 bytes in the immediate value aren't likely to cause a stall, but the size override of using 16 bit registers will always cost a cycle, don't worry about it.

Arthur Kalliokoski
  • 1,455
  • 10
  • 10
  • Operand-size prefixes don't "cost a cycle" on modern x86 (Intel since PPro at least). Unless they cause an LCP stall, there's no extra cost in the front-end. (LCP = length-changing prefix, like for `add cx, 1234` but not `add cx, 12` because it's only imm8 with or without the prefix, see Agner Fog's microarch guide on https://agner.org/optimize/). The other cost is for write-only destinations like `mov ax, 1234`, which has to merge into the old RAX, instead of starting a new dependency chain by zero-extending into the full reg when you write EAX. – Peter Cordes Mar 30 '21 at 12:50
  • [Why doesn't GCC use partial registers?](https://stackoverflow.com/q/41573502) explains how various CPUs handle it. TL:DR: your advice to just use one variable per register is right, but the reason isn't quite right. – Peter Cordes Mar 30 '21 at 12:51