Every program eventually becomes a sequence of instructions executed by the CPU.
Whether you write code in Java, Python, Go, or C, the processor does not understand variables, classes, loops, or functions in the way programmers describe them.
It understands machine instructions: small binary commands such as loading a value, adding two numbers, comparing values, or jumping to another instruction.
Understanding this execution model gives us the foundation for later topics such as processes, context switching, system calls, and scheduling.
Consider this C function:
To a programmer, this means “add two integers and return the result.”
The CPU sees something closer to:
A compiler translates high-level code into machine instructions for a specific processor architecture.
One source file goes in and two different sets of instructions can come out. Different processor architectures use different instruction sets. A program compiled for an x86-64 processor does not automatically contain the correct instructions for an ARM processor.
This is why software is often distributed separately for architectures such as:
The source code may be the same, but it must be compiled into the instruction set understood by the target CPU.
An instruction is a small operation that the processor knows how to perform.
Typical instructions can:
For example, a processor may execute operations conceptually similar to:
Real machine instructions are encoded as binary values. Assembly language provides a readable representation of those instructions.
An assembly instruction may look like:
At a high level, this tells the CPU to add the value in one register to the value in another.
You do not need to become an assembly programmer for this course. The important point is that every high-level operation eventually becomes one or more CPU instructions.
Registers are small, extremely fast storage locations inside the processor.
The CPU uses them to hold values that are needed immediately.
For example, while evaluating:
the processor may place first and second into registers, perform the addition, and keep the result in another register.
Registers are much smaller than main memory. A processor may have billions of bytes of RAM available but only a relatively small set of general-purpose registers.
Some registers have special roles. The diagram shows where they sit relative to the memory a program uses:
The program counter and stack pointer hold addresses, so they point at memory rather than holding program values. The general-purpose registers hold the values themselves, which is why data has to be loaded in before the CPU can work on it and stored back afterward.
The program counter stores the address of the next instruction the CPU should execute.
After most instructions, it advances to the following instruction. A branch or function call can change it to another address.
On different architectures, it may also be called the instruction pointer.
The stack pointer tracks the current top of the program’s stack.
The stack is used for function calls, local variables, return addresses, and temporary values.
These registers hold values used during calculations, address manipulation, function calls, and data movement.
The exact register names and responsibilities depend on the processor architecture.
At a simplified level, the CPU repeatedly performs three steps:
Nothing else drives execution forward: the CPU finishes one instruction, advances the program counter, and begins again, billions of times per second.
The CPU uses the program counter to locate the next instruction.
The instruction is retrieved and brought into the processor.
The CPU determines what the instruction means.
It identifies the operation and the values or registers involved.
For example, the instruction may request an addition, comparison, data transfer, or jump.
The processor performs the requested operation.
It may update a register, read data, write data, or change the program counter.
After the instruction completes, the cycle begins again.
Modern processors perform these steps in far more sophisticated ways. They may overlap multiple instructions, execute some operations in parallel, and predict which branch a program will take.
However, the fetch-decode-execute model remains a useful foundation.
Consider:
A simplified execution might look like this:
The CPU does not understand the full expression as one high-level idea. The compiler converts it into smaller operations supported by the processor.
The compiler may also optimize the expression and directly produce the value 60, because the result can be calculated during compilation.
This is an important distinction:
Source code describes what the program should do. The compiler decides which machine instructions should perform it.
Loading simulation...
Consider this loop:
At a high level, the processor repeatedly performs these operations:
The loop depends on a branch instruction.
A branch changes the program counter so that execution continues from a different instruction.
Without branches, the CPU would only execute instructions in a straight line. Branches make conditionals, loops, function calls, and error handling possible.
Consider:
A simplified instruction flow is:
The comparison updates internal CPU state, commonly represented through condition flags.
A later branch instruction checks those flags and decides which instruction should execute next.
This is how familiar control-flow constructs are implemented using lower-level operations.
A function call temporarily changes the flow of execution.
Consider:
The program must pass the argument, move execution to the square function, and later return to the instruction after the call.
A simplified flow looks like this:
Both jumps in that sequence are just writes to the program counter. The one piece of information that makes the second jump possible is the return location saved in the second step, which is why a corrupted return address turns into a program that resumes somewhere it was never supposed to go.
Function arguments and return values are often passed through registers. The stack may be used when there are too many arguments, when local variables need storage, or when registers must be preserved.
The rules describing how functions pass arguments and return values are called a calling convention.
Compilers follow the platform’s calling convention so that independently compiled code can work together.
Processors operate using a clock.
A CPU advertised at 3 GHz experiences approximately three billion clock cycles per second. However, this does not mean it executes exactly three billion program instructions per second.
Some instructions require more work than others. Memory access can delay execution, while modern processors may complete parts of several instructions during the same cycle.
Performance depends on many factors, including:
This is why clock speed alone is not enough to compare two CPUs or predict how fast a program will run.
The CPU only executes the instructions currently assigned to it.
When several applications are running, the operating system decides which process or thread should use each CPU core.
The processor may execute instructions from one program, pause it, execute instructions from another program, and later return to the first.
To resume a program correctly, the operating system must preserve its execution state, including values such as the program counter and registers.
We will study this mechanism later when discussing processes, scheduling, and context switching.
For now, remember that the CPU executes instructions, while the operating system controls which program’s instructions it should execute.
Create a file named calculation.c:
Compile it normally:
You can view the generated assembly using:
Open the file:
The exact instructions depend on your CPU architecture and compiler.
You can also inspect the final executable with:
The output may initially look unfamiliar. Try to identify:
main functioncalculate functionNow compile with optimization enabled:
Compare the two assembly files:
The optimized version may contain fewer instructions. The compiler can simplify calculations, remove unnecessary work, or inline a function directly into its caller.
This demonstrates that the source code and the final instruction sequence are related, but they are not identical.
When a program is running, think of the CPU as repeatedly asking:
The program counter determines where execution continues. Registers hold the CPU’s immediate working data. Instructions perform small operations, and branches change the flow of execution.
Millions or billions of these small operations combine to create the behavior of an application.
The CPU executes machine instructions, not high-level source code.
A compiler translates source code into instructions for a particular processor architecture. These instructions move data, perform calculations, compare values, call functions, and change the flow of execution.
Registers provide fast storage inside the CPU. The program counter tracks the next instruction, while the stack pointer helps manage function execution.
At a simplified level, the processor repeatedly fetches, decodes, and executes instructions.
The most important idea is:
A running program is ultimately a changing CPU state driven by a sequence of machine instructions.
5 quizzes