views:

445

answers:

3

I was looking through the disassmbly of my program (because it crashed), and noticed lots of

xchg    ax, ax

I googled it and found out it's essentially a nop, but why does visual studio do an xchg instead of a noop?

The application is a C# .NET3.5 64-bit application, compiled by visual studio

+7  A: 

On x86 the NOP instruction is XCHG AX, AX

The 2 mnemonic instructions assemble to the same binary op-code. (Actually, I suppose an assembler could use any xchg of a register with itself, but AX or EAX is what's typically used for the nop as far as I know).

xchg ax, ax has the properties of changing no register values and changing no flags (hey - it's a no op!).


Edit (in response to a comment by Anon.):

Oh right - now I remember there are several encodings for the xchg instruction. Some take a mod/r/m set of bits (like many Intel x86 architecture instructions) that specify a source and destination. Those encodings take more than one byte. There's also a special encoding that uses a single byte and exchanges a general purpose register with (E)AX. If the specified register is also (E)AX hen you have a single-byte NOP instruction. you can also specify that (E)AX be exchanged with itself using the larger variant of the xchg instruction.

I'm guessing that MSVC uses the multiple byte version of xchg with (E)AX as the source and destination when it wants to chew up more than one byte for no operation - it takes the same number of cycles as the single byte xchg, but uses more space. In the disassembly you won't see the multiple byte xchg decoded as a NOP, even if the result is the same.

Specifically xchg eax, eax or nop could be encoded as opcodes 0x90 or 0x87 0xc0 depending on whether you want it to use up 1 or 2 bytes. The Visual Studio disassembler (and probably others) will decode the opcode 0x90 as the NOP instruction and will decode opcode 0x87 0xc0 as xchg eax, eax.

It's been a while since I've done detailed assembly language work, so chances are I'm wrong on at least one count here...

Michael Burr
Then why do I also see nop?
Malfist
That would be the disassembler being smart (or dumb, depending on what you wanted it to do). Do you see both decodes using the same disassembler? That might be something interesting to look at.
Michael Burr
It's the visual studio disassembler...
Malfist
`XCHG` only operates on `(E)AX`.
Anon.
My guess is that the disassembler has access to meta-data that tells it what the original instruction was, but it's a guess.
Lasse V. Karlsen
Reminds me of the NOP on 360 and 370 machines: "BCR 0,0". BCR is a conditional branch to the address in a register. The first 0 is the condition code mask with all conditionsl masked out, so it BCR 0,0 was "unconditionally, don't branch". The second zero was for register zero; when used in addressing, register zero was always replaced with the value "zero". So the NOP was "unconditionally, don't branch to address 0".
Cylon Cat
Also, is `xchg ax, ax` does nothing, why does `xchg bx, bx` map to something else (0x87 0xdb)? Shouldn't it also do nothing? Mighty strange if you ask me.
Lasse V. Karlsen
x86 has a lot of "shortcuts" for specific registers - `0x90` is " `XCHG` register with `(E)AX` ".
Anon.
@Lasse: Anon is right about this - I've updated the answer to cover the single byte/multiple byte encoding details of `xchg`.
Michael Burr
+3  A: 

xchg ax,ax and nop are actually the same instruction, they map to the same opcode (0x90 iirc). That's fine, xchg ax,ax is a No-Op. Why should one waste extra opcode encodings with instructions that don't do anything?

Questionable is why you see both mnemonics printed. I guess it's just a flaw in your disassembly, there is no binary difference.

Alexander Gessler
0x90 not 90... ..
Earlz
A: 

MSVC puts NOPs in the compiled code for debug builds, generally. This allows Edit & Continue to work.

Alex