35

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

Crashworks
  • 40,496
  • 12
  • 101
  • 170
Malfist
  • 31,179
  • 61
  • 182
  • 269
  • 1
    are the bytes associated with these instructions the same? nop should be hex 90. is xchg ax,ax a 2 byte instruction? – John Knoeller Jan 25 '10 at 22:30
  • This turned out to be a question with a bit more nuance than I originally thought (or remembered anyway). – Michael Burr Jan 26 '10 at 01:26
  • hey can you tell me how to look at assembly code like yours. i'd like to see how a simple prgoram is converted to assembly. thanks – masfenix Jan 26 '10 at 02:03

6 Answers6

45

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

T Percival
  • 8,526
  • 3
  • 43
  • 43
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • 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 Jan 25 '10 at 22:17
  • `XCHG` only operates on `(E)AX`. – Anon. Jan 25 '10 at 22:17
  • 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 Jan 25 '10 at 22:19
  • 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 Jan 25 '10 at 22:20
  • 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 Jan 25 '10 at 22:22
  • 2
    x86 has a lot of "shortcuts" for specific registers - `0x90` is " `XCHG` register with `(E)AX` ". – Anon. Jan 25 '10 at 22:24
  • @Lasse: Anon is right about this - I've updated the answer to cover the single byte/multiple byte encoding details of `xchg`. – Michael Burr Jan 25 '10 at 23:03
  • The multple byte nop could be used to align things in the pipeline could it not? I seem to remember some mention of this in one of Michael Abrash's optimization articles. Ack, I guess I get the question necro badge... I didn't notice the age. – JimR Dec 04 '10 at 23:31
  • I can't source it, but I believe that modern Intel x86 processors do actually have a no-register NOP so as to avoid the (wasteful?) overhead incurred from touching the registers unlike the old ones which were originally just symbols. – Rushyo Jan 12 '11 at 15:54
12

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
  • 45,603
  • 7
  • 82
  • 122
  • The question of whether to waste opcode encodings on do-nothing instructions raises some interesting issues, especially in architectures which are specified to trap invalid instructions. On the one hand, if a significant portion of the opcode space is used by functionally-identical instructions, forbidding the use of all but one such encoding would make the others available for future operations. On the other hand, doing so could make some dynamic-code-generation scenarios more difficult, and if invalid opcodes are supposed to be trapped, require extra hardware to trap them. – supercat Apr 09 '12 at 23:21
  • @supercat: That's a bit different than just declining to define *yet another* opcode that does nothing, isn't it? – SamB May 02 '15 at 19:29
  • @SamB: Suppose a processor that uses 16-bit instructions defines 256 opcodes to mean "add an 8-bit immediate operand to r0" and another 256 to mean "subtract an 8-bit immediate operand from r0". The instructions which add zero and subtract zero may be functionally equivalent, but outlawing one of them might complicate code that expects to dynamically modify an "add N to r0" instruction that's used within a tight loop so as to avoid the need to dedicate a register to the addend (or have to load it within the loop). If there were only 255 "add N to r0" instructions... – supercat Jul 30 '15 at 18:41
  • ...code which might want to change the value of N to or from zero might have to special-case that behavior, rather than simply being able to store the desired N within 8 bits of the opcode and ignoring the rest. – supercat Jul 30 '15 at 18:42
  • @supercat: That's hardly the same as deciding not to invent a whole new encoding just for NOP, though – SamB Aug 01 '15 at 22:45
8

Actually, xchg ax,ax is just how MS disassembles "66 90". 66 is the operand size override, so it supposedly operates on ax instead of eax. However, the CPU still executes it as a nop. The 66 prefix is used here to make the instruction two bytes in size, usually for alignment purposes.

Igor Skochinsky
  • 24,629
  • 2
  • 72
  • 109
  • there are also many other NOPs with different sizes and the compiler can choose the one that fits best http://stackoverflow.com/a/12564044/995714 – phuclv Aug 23 '14 at 05:25
  • Any idea why MSVC emits NOPs in the optimized release build? Does x86_64 require alignment for some specific instructions? The instruction following the `xchg ax, ax` in my code is a simple `movzx`, surely it has no special alignment requirements? – Violet Giraffe May 20 '22 at 22:05
  • 1
    @VioletGiraffe I can’t say without seeing your code. For example, often some nops are inserted to align a loop start for faster execution. If you wan to know the answer for your specific case, ask a new question and add the code. – Igor Skochinsky Jun 08 '22 at 06:59
5

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

Alex Budovski
  • 17,947
  • 6
  • 53
  • 58
2

I don't know if it has something to do with the question but many Windows functions begin with MOV EDI, EDI. That's also a 2 byte NOP. Two bytes NOP's are useful to hotpatch code, because you can safely replace it with a short JMP.

Reference: http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx

Guest
  • 21
  • 1
1

The real question here is; why did the disassembler choose to display it as xchg ax,ax and not nop?

I suspect that this is from 32-bit or 64-bit code and (given that the disassembler displayed xchg ax,ax and not xchg eax,eax) that there's an operand size override prefix intended to make the nop slightly larger (to achieve a certain amount of padding with fewer instructions); and the existence of prefixes has confused the disassembler resulting in xchg ax,ax (rather than nop or o16 nop).

Brendan
  • 35,656
  • 2
  • 39
  • 66