RoadToChain Logo
RoadToChain
T1/M1.3/How the EVM executes code — the virtual stack machine
beginner 15m read

How the EVM executes code — the virtual stack machine

Deep dive into the Ethereum Virtual Machine: stack depth, memory allocations, persistent storage slots, and instruction execution cycles.

#EVM #bytecode #execution

Let's address a low-level compiler warning that leaves most smart contract developers completely baffled:

CompilerError: Stack too deep. Try compiling with type adjustments.

You stare at your screen in confusion: My function only has six local variables! My computer has 32 gigabytes of RAM. Why is the compiler complaining about "depth" when I have enough memory to run a graphics engine?

I genuinely thought this too. I was treating my Solidity contract like standard Web2 code running on limitless cloud hardware. In reality, you are writing code for a highly specialized, globally distributed virtual machine with extreme, custom constraints: the Ethereum Virtual Machine (EVM).

To write gas-efficient, secure smart contracts, you must understand exactly how a validator's CPU processes your code.


1. The Metaphor: The Stressed Kitchen

Think of the EVM as a high-volume, extremely cramped restaurant kitchen running with tight, hard-coded rules:

  • The Stack (The Chef's Hands): The chef can only hold up to 16 active plates or items at once. If they try to carry 17, they drop everything. This is what triggers the dread Stack too deep compiler error. Stack access is free and lightning-fast, but extremely limited.
  • Memory (The Prep Table): Where you chop carrots and assemble dishes temporarily. Cleaned completely at the end of every order. Cheap to use, but the landlord charges quadratic rent—if you ask for a table twice as big, it costs four times as much gas.
  • Storage (The Cold Vault Across the Street): This is where you keep goods long-term. Every trip across the street takes serious energy (gas). Each ingredient rents a sequential locker room slot permanently.

// Reality Check

Many developers treat Memory like a standard array heap. Because Memory costs scale quadratically, allocating a huge array (e.g. uint256[] memory data = new uint256[](10000)) inside a function can easily cost more in gas fees than writing to Storage. Always size memory allocations conservatively.

— Production Engineering Principle

2. The Core Architecture: Three Storage Regions

The EVM doesn't have a single shared heap memory. Instead, it operates with three completely separate memory areas. Each has a different lifecycle, access speed, and gas cost:

How the EVM Executes Code — The Stack Machine
The EVM executes bytecode opcodes using a LIFO Stack for computations, volatile Memory for temporary arrays, and persistent Storage slots for state variables.

1. The Stack

  • How it works: A standard Last-In-First-Out (LIFO) stack. Think of a stack of plates. You can only add or remove a plate from the very top.
  • The limit: The stack has a maximum depth of 1024 items. Each item is exactly 32 bytes (256 bits).
  • The catch: You can only reach the top 16 items. If your function tries to keep more than 16 local variables active at once, it can't reach them. This triggers Stack too deep.

2. Memory

  • How it works: A clean, temporary, linear byte array.
  • The limit: Completely wiped clean the second your transaction finishes executing.
  • The cost: Cheap to read and write, but it has a quadratic penalty. Allocating massive arrays inside a single call becomes exponentially more expensive, designed to prevent spammers from hogging validator RAM.

3. Storage

  • How it works: A massive, permanent key-value store mapping 32-byte slots to 32-byte values.
  • The limit: Permanent. Every state change is written to the hard drives of thousands of validators worldwide.
  • The cost: Extremely expensive.

// I Got This Wrong

In my first dApp, I declared three sequential variables in a state contract without thinking about slot packing. It cost my users 60,000 gas to initialize the contract. By packing them into a single slot, I dropped the setup cost by 66%. Always order your variables by size sequentially!

— Postmortem Confession

3. The Instruction Cycle (Opcodes)

At the lowest level, the EVM compiles your Solidity code into a string of single-byte instructions called Opcodes (Operations).

Let's look at how the EVM executes a simple addition 3 + 5 using its stack:

SmartAccount.sol
[ PUSH1 0x03 ] ──> Pushes value 3 onto the Stack.          Stack: [ 3 ]
[ PUSH1 0x05 ] ──> Pushes value 5 onto the Stack.          Stack: [ 5, 3 ]
[ ADD ]        ──> Pops top two values (5 and 3),          Stack: [ 8 ]
                   adds them, and pushes result (8).

Every single opcode has a fixed gas cost based on the CPU computational strain it places on validator hardware:

  • ADD costs exactly 3 gas.
  • MSTORE (writing 32 bytes to Memory) costs 3 gas.
  • SSTORE (permanently renting/writing a Storage Slot room) costs up to 20,000 gas.

4. Real-World Gas Optimization: Storage Packing

Because each slot room is exactly 32 bytes, Solidity compiles variables sequentially. If you order them poorly, you waste slots:

SmartAccount.sol
solidity
// ❌ INEFFICIENT: Takes 3 separate slots (Rents 3 Rooms = 60,000+ gas)
uint128 public a; // 16 bytes (Slot 0)
uint256 public b; // 32 bytes (Slot 1 - uint256 takes the entire slot!)
uint128 public c; // 16 bytes (Slot 2)

By grouping variables by size, the compiler packs them into single slots:

SmartAccount.sol
solidity
// ✅ EFFICIENT: Packs 'a' and 'c' into Slot 0. Takes only 2 slots (Rents 2 Rooms = Saves 20,000 gas!)
uint128 public a; // 16 bytes (Slot 0)
uint128 public c; // 16 bytes (Slot 0 - Packed!)
uint256 public b; // 32 bytes (Slot 1)

System Design Challenge
Think Active

Look at the evm.codes interactive website. Locate the gas cost for SLOAD (reading a storage slot) versus MLOAD (reading a memory byte). Calculate how many memory reads you can perform for the cost of a single storage read.

[ Think Before Continuing ]

Was this lesson helpful?

Let us know what you think of this specification. (submitting anonymously)