23

As the title states, why would one use "movl $1, %eax" as opposed to, say, "movb $1, %eax", I was told that movl would zero out the high order bits of %eax, but isn't %eax a register that's equivalent to the size of the system's wordsize? meaning that movl is in actuality an integer operation (and not a long?)

I'm clearly a bit confused about it all; Thanks.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847

6 Answers6

45

opposed to, say, "movb $1, %eax"

This instruction is invalid. You can't use eax with the movb instruction. You would instead use an 8-bit register, or write the full register with a value that has the value you want in the low byte(s) you care about. For example:

movb  $1, %al        # AL = 1, merged with existing 3/7 bytes of previous EAX/RAX
movl  $1, %eax       # AL = 1, AX = 1, EAX = 1, RAX = 1

but isn't %eax a register that's equivalent to the size of the system's wordsize?

No. EAX will always be a 32-bit value, regardless of what mode you're in.

You are confusing C variable sizes with register sizes. C variable sizes may change depending on your system and compiler.

Assembly is simpler than C. In GAS AT&T assembly, instructions are suffixed with the letters "b", "s", "w", "l", "q" or "t" to determine what size operand is being manipulated. (Or a register operand can imply a size, like mov $1, %eax implies movl, unlike with mov $1, (%rdi) which is ambiguous.

  • b = byte (8 bit)
  • s = single (32-bit floating point) used only for x87 instructions like flds (mem)
  • w = word (16 bit)
  • l = long (32 bit doubleword integer), or x87 64-bit floating point
  • q = quad-word (64 bit)
  • t = ten bytes (80-bit floating point)

These sizes are constant. They will never be changed. al is always 8-bits and eax is always 32-bits.

A "word" is always 16 bits in x86 terminology, and in modern x86 is unrelated to the concept of CPU or machine word size; x86 isn't a word-oriented architecture.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Jason
  • 2,341
  • 17
  • 14
7

%eax is a 32-bit register. To use a smaller width, you need %ax for 16-bits. %ax can be further divided into %ah for the high byte of %ax, and %al for the lower byte. The same goes for the other x86 GPRs.

Looking at the Intel instruction set reference for the mov instruction, I don't see a variant that can move a single byte into a 32-bit register -- it's probably interpreted as a move into %al.

Since movl is a 32-bit instruction, the values for the upper bytes will correspond to zeros in the case of an immediate value. If you were moving from memory you would be moving an entire 32-bit word.

%eax is not zeroed out unless you either movl $0, %eax, or if you xorl %eax, %eax. Otherwise it holds whatever value was previously in there. When you movl $1, %eax, you will end up with 0x00000001 in the register because the 32-bit instruction moves a 32-bit immediate value into the register.

Aaron Klotz
  • 11,287
  • 1
  • 28
  • 22
  • That movl is a 32 bit instruction is partly the answer I was looking for, but another answer I'm asking for is whether or not %eax by default is _not_ zeroed out, or is undefied? – Negative Acknowledgement Dec 14 '09 at 04:55
  • 2
    %eax is never zeroed out unless you explicitly do so. If you use movb to just write into one of the bytes, the other bytes are unchanged. – Victor Shnayder Dec 15 '09 at 19:18
  • @NegativeAcknowledgement [How do AX, AH, AL map onto EAX?](https://stackoverflow.com/q/15191178) explains how partial-register writes affect the full register. (Only writing a 32-bit register implicitly zero-extends to the full 64-bit. Otherwise it just merges a new part of the register with the old 386 / 8086 semantics.) – Peter Cordes Feb 13 '23 at 06:15
7

On a 32 bit machine, %eax is a 4 byte (32 bit) register. movl will write into all 4 bytes. In your example, it'll zero out the upper 3 bytes, and put 1 in the lowest byte. The movb will just change the low order byte.

Victor Shnayder
  • 103
  • 1
  • 6
  • So movl is a 4 byte operation? – Negative Acknowledgement Dec 14 '09 at 04:09
  • Not sure which assembler this is, but isn't the only difference the size of the constant? – peterchen Dec 15 '09 at 13:33
  • 2
    Yes. In the IA32 architecture (usually called x86), "long" means 4 bytes, and movl stands for "move long". – Victor Shnayder Dec 15 '09 at 19:17
  • 1
    @Negative Acknowledgement: You're asking the wrong question. Every operation on a system is in multiples of the word size. On a 32-bit system, every operation works on 32 bits. The way the shorter (`movb`) instruction works is by AND'ing the first byte then adding your value, thus it is slow. You generally always want to work on full words or you'll incur speed penalties (at the cost of more memory used, of course). – Blindy May 22 '10 at 22:17
4

long was originally 32 bits, while int and short were 16. And the names of the opcodes don't change every time someone comes out with a new operating system.

Anon.
  • 58,739
  • 8
  • 81
  • 86
  • 2
    What has a release of an operating system got to do with the naming conventions of types? – Negative Acknowledgement Dec 14 '09 at 04:10
  • It was a figure of speech. The intent was to indicate that once published, the instruction set specification isn't destructively changed. – Anon. Dec 14 '09 at 04:20
  • 2
    This is why I prefer Intel syntax to GAS syntax. Intel: mov dword eax, ecx GAS: movl eax, ecx The "l" size operand confuses people. More people get that 'dword' means 32-bits. – Jason Dec 14 '09 at 04:28
  • Looks all the same to me, a dword (double word) suggests that the word size is 16 bits... – Negative Acknowledgement Dec 14 '09 at 04:53
  • The word size *was* 16 bits on the 8086. – Anon. Dec 14 '09 at 05:38
  • Uh, isn't x86 equivalent to 8086? isn't this what we're dealing with, and isn't it a 32 bit architecture? – Negative Acknowledgement Dec 14 '09 at 06:36
  • The 8086 processor was quite definitively 16-bit. – Anon. Dec 14 '09 at 06:44
  • @NegativeAcknowledgement: In Intel documentation / terminology, "word" means 16-bit. Always and forever. Not depending on what mode you're running in. This is quirky and "exposes" x86-64's 16-bit heritage, but so what? Now we have names for all power of 2 sizes. SIMD instruction names are easy to understand/remember, like [`packssdw`](https://www.felixcloutier.com/x86/packsswb:packssdw) (pack with signed saturation from dword to word elements). ISAs that started as 32-bit (like MIPS and ARM), often use `h` = half-word, e.g. MIPS `lhu` load half-word with zero-extension. – Peter Cordes Oct 31 '19 at 06:21
3

Your second choice will just produce an error, x86 doesn't have that instruction. X86 is a bit unique with respect to loading bytes into certain registers. Yes, on most instruction set architectures the operand is zero or sign-extended, but x86 allows you to write just the lower byte or lower 16 bits of some of them.

There are certainly other choices, like clearing the register and then incrementing it, but here are three initially reasonable-looking choices you have:

   0:   b8 01 00 00 00          movl   $0x1,%eax

   5:   31 c0                   xorl   %eax,%eax
   7:   b0 01                   movb   $0x1,%al

   9:   b0 01                   movb   $0x1,%al
   b:   0f b6 c0                movzbl %al,%eax

The first is 5 bytes, the second 4, the third 5. So the second is the best choice if optimizing for space, otherwise I suppose the one most likely to run fast is the first one. X86 is deeply pipelined these days, so the two instructions will interlock and the machine may need quite a few wait states depending on details of the pipeline hardware.

Of course, these x86 ops are being translated in CPU-specific ways into CPU micro-ops, and so who knows what will happen.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • For future reference, if you're optimizing for code-size you'd use xor-zero (2 bytes) + `inc %eax` (1 byte in 32-bit mode). Or `push imm8` / `pop r32` or `pop r64`. Or `lea reg, [reg+disp8]` from another reg with a known value. [Tips for golfing in x86/x64 machine code](//codegolf.stackexchange.com/a/132985). For speed, only the `lea` is worth considering, otherwise use `mov` with a 32-bit immediate. – Peter Cordes Oct 31 '19 at 06:24
2

%eax is 32 bits on 32-bit machines. %ax is 16 bits, and %ah and %al are its 8-bit high and low constituents.

Therefore movl is perfectly valid here. Efficiency-wise, movl will be as fast as movb, and zeroing out the high 3 bytes of %eax is often a desirable property. You might want to use it as a 32-bit value later, so movb isn't a good way to move a byte there.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412