Abstract:
Which instructions and addressing modes are used most often. What is the average instruction length.

Created by Peter Kankowski
Last changed
Filed under Assembly language and machine code

Share on social sitesReddit Digg Delicious Buzz Facebook Twitter

x86 Machine Code Statistics

Which instruction is the most common one in your code? In this test, three popular open-source applications were disassembled and analysed with a Basic script:

All programs were developed with Microsoft Visual C++ 6.0.

Most frequent instructions

Top 20 instructions of x86 architecture: mov constitutes 35% of all instructions, push do 10%, call do 6%, cmp do 5%, add, pop, and lea do 4%

The most popular instruction is MOV (35% of all instructions). Note that PUSH is twice more common than POP. These instructions are used in pairs for preserving EBP, ESI, EDI, and EDX registers across function calls, and PUSH is also used for passing arguments to functions; that's why it is more frequent. CALLs to functions are also very popular.

More than 50% of all code is dedicated to moving things between registers and memory (MOV), passing arguments, saving registers (PUSH, POP), and calling functions (CALL). Only 4th instruction (CMP) and the following ones (ADD, LEA, TEST, XOR) do actual calculations.

From conditional jumps, JE and JNE (equal and not equal) are the most popular. CMP and TEST are commonly used to check conditions. The percentage of the LEA instruction is surprisingly high, because MS VC++ compiler generates it for multiplications by constant (e.g., LEA eax, [eax*4+eax]) and for additions and subtractions when the result should be saved to another register, e.g.:

LEA eax, [ecx+04]
LEA eax, [ecx+ecx]

The compiler also pads the code with harmless forms of LEA (for example, the padding may be LEA edi, [edi]). As is easy to see, the top 20 instructions include all logical operations (AND, XOR, OR) except NOT.

Though LAME encoder uses MMX technology instructions, their share in the whole code of the program is very low. Two FPU instructions (FLD and FSTP) appears in the top 20.

But what about other instructions? It turns out that multiplication and division are very rare: IMUL takes 0.13%, IDIV takes 0.04%, and both MUL and DIV do 0.02%. Even string operations such as REPZ SCASB or REPZ MOVSB are more common (0.32%) than all IMULs and IDIVs. On the contrary, FMUL is more common than FADD (0.71% versus 0.27%).

Average instruction length

Distribution by length: one-byte instructions are seen in 16% of cases, two-byte ones are seen in 29% of cases, three-byte ones are seen in 20% of cases. Instruction length is from 1 to 11 bytes.

75% of x86 instructions are shorter than 4 bytes. But if you multiply the percentage by length, you will find that these short instructions take only 53% of the code size. So another half of a typical executable file consists of instructions with 32-bit immediate values, which are 5 bytes or longer.

The number and type of operands

Number of operands: one-operand instructions make 37% and two-operand instructions make 60% Operand types: immediates constitute 20%, register operands constitute 56%, absolute addresses do 1%, and indirect addresses do 23%

Here are some examples of operand types:

  • immediate: 00000008, 00401024;
  • register: eax, esp;
  • absolute address: dword[00401024], byte[00401024];
  • indirect address: dword[esp + 10], dword[00401024 + eax * 4];

The parser is fairly limited and operands of the JMP and CALL instructions are counted as immediate, while in fact they are absolute addresses. Still you can see that most operands are registers. Global variables are rare in modern programs.

Instruction formats

Instruction formats: 'register-memory' format makes 35%, 'register-register' format makes 27%, 'register-immediate' makes 16%, 'memory-register' format makes 15%, and 'memory-immediate' format makes 7%

Examples of these instructions:

  • register-memory: ADD eax, [esp + 10]; MOV eax, [00401024]
  • register-register: ADD eax, ecx
  • register-immediate: CMP eax, 10
  • memory-register: MOV [esp + 10], eax; MOV [esi + ecx * 4], eax
  • memory-immediate: MOV [esp + 10], 0

Conclusion

Certainly, some observations are true only for MSVC++ compiler. Other compilers will use other instructions; for example, some of them can't do the trick with LEA instruction, and they will use IMUL or MOV/ADD instead. But you can see several general trends: most instructions have 2 operands; memory-register format is less frequent than register-memory; MOV is the most popular instruction and so on.

Download source code (Basic, AWK) and Excel sheet with all data (19 Kb)

Peter Kankowski
Peter Kankowski

About the author

Peter is the developer of Aba Search and Replace, a tool for replacing text in multiple files. He likes to program in C with a bit of C++, also in x86 assembly language, Python, and PHP.

Created by Peter Kankowski
Last changed

15 comments

Ten recent comments are shown below. Show all comments

Vladimir Sedach,

Peter, run-time stats is close to your data. See

"Analysis of x86 Instruction Set Usage for DOS/Windows Applications..." by Ing-Jer Huang and Tzu-Chin Peng

at

http://esl.cse.nsysu.edu.tw/publications/paper/conference/Analysis%20of%20x86%20Instruction%20Set%20Usage%20for%20DOS%20Windows%20Applications%20and%20Its%20Implication%20on%20Superscalar%20Design.pdf

Peter Kankowski,
Vladimir, many thanks for the link!
G.Nitz,

That was a very interesting thing I never read about before. Thanks a lot for it.

Mr. G.

Sirmabus,

I went searching for instruction statistics and luckily your page came out at the top.

I wasn't surprised when I saw it was from your site that I've visited often.

Very useful information, presented in a way easy to grasp with your graphs, etc.

Raymond, your post was years ago but anyhow, your sort of comparing "comparing apples to oranges" here.

Better yet like comparing "Red" to "Gravenstein" (different) apples, as he says says here "..most common one in your code". Which implies a context of the instructions a compiler generates, dynamic as in measuring the qualities of running could is IMHO a distinctly different context.

And incidentally would be hard, if not impossible to trace dynamically in real time. You are talking with an emulator or tracer an exponential slow down that could alter the code flow of the target (as it compensates for the slow down, order of it's message flows are different, etc). Unless perhaps using some sort of ICE setup.

Spasiba moi droog,

Ali,

Hi

Do you have these statistics for ARM processors?

Peter Kankowski,
Hi Ali, unfortunately, this article is about x86 only. My experience with ARM is limited.
Jay Craswell,

Hello Peter I wanted to thank you for doing this work I was planning to do something like this but now I don't have to! I'm curious if you have done this with any recient examples of code?

Other then your study there is only one other that I've heard of but got nothing but 404 messages from the links. Can it be that no one is interested in whats really being used?

I'm trying to find a study on how many API calls are used by typical applications. I've only been looking for an hour but nothing...

If anyone has found one please send a message to j dot craswell at the gee male period commie address.

Re the Comparing apples to oranges I think you miss the point. It is to study which instructions are used (Or not) in the source of a program. How often its used in a loop is (to me) of little to no interest. You have endless examples of horrible code that goes through loops so often that it would make instructions in that subroutine seem more important then they are. On that topic a short story. I was asked to "fix" a large assember program written in 8051. The author was fond of repeating the same code over and over and sometimes calling and returning other times inline and that was the good part of his software "riting" skills. His boss kept calling him "Mental Patient XXX" and I thought this was a slur. Later I decided it would be an insult to Mental Patients but... I learned he really was a mental patient. SIGH. I get all the good jobs. I offered to do the project for half but only if I didn't have to look at the original source. *I also ended up designing the electronics hardware. The guy I did the work for built a cabnet for use in industrial settings and I truly believe you could drive over it with a car and not damage it.

In regards to doing this experiment again I had a talk with a mentor who tells me that the i7, i9? have some pretty amazing instructions available that he thinks aren't used at all.

I'm VERY interested if you (anyone?) know where there is a real Hardware Emulator available? The ones I've used in the past were always able to run at full speed (Or so close it was rarely an issue) The last I heard these are not available for the modern x86 machines anymore. "Illegal" according to one of the guys I used to work with. Or so I was told to my great surprise and horror. I often write firmware and its always sped up development to be able to see what its REALLY doing ha ha... *My code one thing the actual action maybe not the same. Plus firmware is unlikely to be allowed to get updated so its important to get it right the first time and this has always been my favorite crutch.

Peter Kankowski,

Hello Jay, I plan to update these statistics and other articles, but unfortunately I'm quite busy with the other work now, so it will not happen soon.

Under Linux, you could try strace to collect the API calls statistics.

Ben,

I would really appreciate it if you had collected all the branch instructions together as a statistic. (Including all the Jxx, Call, etc).

Santiago Fiorino,

gracias capo

Your name:


Comment: