You can scroll through the presentation as you watch the video here:
In this article, we will dive even deeper into the Ethereum Virtual Machine through topics of the memory hierarchy and instruction set.
Our topics are getting increasingly more in-depth than the ones we touched on so far, but learning about them will be very beneficial as we continue towards our goal of putting together the pieces of our Solidity puzzle.
🌍 Full Course Curriculum and Overview: Solidity Crash Course
This is Part 5 of the Solidity Crash Course referenced in the previous paragraph.
The EVM Memory Hierarchy
So far, we have been discussing the EVM from a higher point of view, focusing on general terms, and also touching on both Bitcoin and Ethereum working principles.
We will continue with EVM architecture specifics, starting with EVM’s three data areas:
- storage,
- memory, and
- the stack.
Of course, there will be much more talk about the memory and the instruction set in the future, but our current goal is to build ourselves an outline of the concept sprinkled with enough details to support our journey.
Storage
As mentioned in the previous article, each account has a dedicated, persistent data area called storage.
Persistence means that storage will exist even when functions finish their execution or when transactions finalize (regardless of their success or error).
Storage is implemented as a key-value map (e.g. dictionary in Python) between 256-bit words to 256-bit words. Some downsides worth mentioning regarding using storage are the inability to enumerate storage from within a contract, high reading price, and even higher initialization and modification price (of course, in units of gas).
With that in mind, as programmers, we should be very observant and try to minimize the use of storage. Storage is commonly used for storing data like derived calculations, caching, and results of aggregate operations.
💡 Note: a contract can access and modify only its own, dedicated storage.
Memory
Memory is a second data area in EVM’s hierarchy, and a new instance of memory is attached to each message call. Memory is linear, meaning that “memory appears to the program as a single contiguous address space”
ℹ️ (Gonzalez, Antonio; Latorre, Fernando; Magklis, Grigorios (2011). Processor Microarchitecture: An Implementation Perspective. Morgan & Claypool Publishers. p. 72.).
Some properties of linear address space are the simple interface for programmers, clean design, flexibility due to uniform access speed (in contrast with segmented/paged memory address space, where page switches cause non-uniform access speed), and maximum execution speed.
Memory is addressed at a byte (8-bit) level, but its reads are limited to a width of one word (256 bits). This limitation enables us to address and modify a single byte of a word or a whole word at once.
The data area of memory is expanded by one word (256 bits) on each attempt of reading from or writing to a previously unreferenced address location.
💡 Note: When the expansion occurs, its cost is deducted (paid) from the total amount of gas allocated to a transaction. The price of memory increases quadratically with the size of the memory extension.
Stack
It is important to note that, unlike the register-based computers we use daily, the EVM is a stack-based machine.
This means that all computations are performed on a data structure called the stack. The stack can hold at most 1024 elements, comprised of words of 256 bits. Accessing the stack is a bit unusual, but we’ll get used to the two approaches available: copy and swap approach.
- With copy approach, we’re restricted to seeing the top 16 elements of the stack, and we can copy one of them to the top of the stack.
- With the swap approach, we can swap the topmost element with one of the 16 elements below it.
In both cases, our operations were performed over 16+1 elements. Every other available approach just takes the topmost one or two elements, i.e. operands from the stack, performs the operation, and stores the result on top of the stack.
💡 Note: it is also possible to access the elements stored deeper on the stack by temporarily placing the upper elements in memory or storage, but we would have to do so in only one way: in a top-down manner, and we should also replace the removed elements afterward.
Instruction Set
There is a very good definition of the instruction set architecture, stating that
“An Instruction Set Architecture (ISA) is part of the abstract model of a computer that defines how the CPU is controlled by the software. The ISA acts as an interface between the hardware and the software, specifying both what the processor is capable of doing as well as how it gets done” (https://www.arm.com/glossary/isa).
It may seem mouthful, but let’s break the definition down into key elements.
The first element is, of course, the instruction set architecture, which defines how the software controls the CPU. Although we’re currently not concerned with such low-level details of EVM, let’s just keep in mind that there is a virtual CPU in the EVM that executes the instructions (https://www.techtarget.com/whatis/definition/instruction). In the context of EVM, an instruction set is a catalog of all the instructions available for use in EVM.
The second element is an abstract model of a computer, which is implemented as EVM.
The third element is software, represented by EVM instructions, in most cases compiled from the developer’s source code, e.g. Solidity. In some rare cases, someone even might want to write the machine code directly. The EVM’s instruction set is minimized with the goal of making a consensus process safe and secure, i.e. by avoiding incorrect or inconsistent implementations.
The EVM instruction set works with basic data types i.e. 256-bit words or on slices of memory or other byte arrays. The instruction set includes the usual operations, such as those for arithmetic, bit, logic, and comparison (https://docs.soliditylang.org/en/v0.8.15/yul.html#opcodes, https://www.ethervm.io/#opcodes).
There are instructions supporting conditional and unconditional jumps and instructions for accessing the most common properties of smart contracts, e.g. the contract number and timestamp.
The Conclusion
In this article, we went even further into the details of the Ethereum Virtual Machine, with special attention given to memory hierarchy and instruction set.
- First, we observed that the EVM memory hierarchy is made of three data areas: storage, memory, and the stack. Metallica was wrong: memory does not remain.
- Second, we gained an insight into how the storage is implemented, along with some of its upsides, downsides, and persistence property. But storage does remain – especially if it’s rented at Davy Jones’ Locker.
- Third, we pointed out the EVM as a stack machine vs. a more common, register-based machine runtime environment. Also, we went through the most commonly used ways of accessing the elements on the stack. Our odds are definitely stacked.
- Fourth, we sliced and diced the Instruction Set Architecture definition and walked the plank. We fell right into the deepest depths of the EVM instruction set.