Skip to main content
Author: Nikolai Durov
Date: March 23, 2020
: Original whitepaper, PDF

Abstract

The aim of this text is to provide a description of the Telegram Open Network Virtual Machine (TON VM or TVM), used to execute smart contracts in the TON Blockchain.

Introduction

The primary purpose of the Telegram Open Network Virtual Machine (TON VM or TVM) is to execute smart-contract code in the TON Blockchain. TVM must support all operations required to parse incoming messages and persistent data, and to create new messages and modify persistent data. Additionally, TVM must meet the following requirements:
  • It must provide for possible future extensions and improvements while retaining backward compatibility and interoperability, because the code of a smart contract, once committed into the blockchain, must continue working in a predictable manner regardless of any future modifications to the VM.
  • It must strive to attain high “(virtual) machine code” density, so that the code of a typical smart contract occupies as little persistent blockchain storage as possible.
  • It must be completely deterministic. In other words, each run of the same code with the same input data must produce the same result, regardless of specific software and hardware used.1
The design of TVM is guided by these requirements. While this document describes a preliminary and experimental version of TVM,2 the backward compatibility mechanisms built into the system allow us to be relatively unconcerned with the efficiency of the operation encoding used for TVM code in this preliminary version. TVM is not intended to be implemented in hardware (e.g., in a specialized microprocessor chip); rather, it should be implemented in software running on conventional hardware. This consideration lets us incorporate some high-level concepts and operations in TVM that would require convoluted microcode in a hardware implementation but pose no significant problems for a software implementation. Such operations are useful for achieving high code density and minimizing the byte (or storage cell) profile of smart-contract code when deployed in the TON Blockchain.

1 Overview

This chapter provides an overview of the main features and design principles of TVM. More detail on each topic is provided in subsequent chapters.

1.0 Notation for bitstrings

The following notation is used for bit strings (or bitstrings)—i.e., finite strings consisting of binary digits (bits), 0\texttt{0} and 1\texttt{1}—throughout this document.

1.0.1. Hexadecimal notation for bitstrings

When the length of a bitstring is a multiple of four, we subdivide it into groups of four bits and represent each group by one of sixteen hexadecimal digits 0\texttt{0}9\texttt{9}, A\texttt{A}F\texttt{F} in the usual manner: 0160000\texttt{0}_{16}\leftrightarrow\texttt{0000}, 1160001\texttt{1}_{16}\leftrightarrow\texttt{0001}, \ldots, F161111\texttt{F}_{16}\leftrightarrow\texttt{1111}. The resulting hexadecimal string is our equivalent representation for the original binary string.

1.0.2. Bitstrings of lengths not divisible by four

If the length of a binary string is not divisible by four, we augment it by one 1\texttt{1} and several (maybe zero) 0\texttt{0}s at the end, so that its length becomes divisible by four, and then transform it into a string of hexadecimal digits as described above. To indicate that such a transformation has taken place, a special “completion tag” _\texttt{\_} is added to the end of the hexadecimal string. The reverse transformation (applied if the completion tag is present) consists in first replacing each hexadecimal digit by four corresponding bits, and then removing all trailing zeroes (if any) and the last 1\texttt{1} immediately preceding them (if the resulting bitstring is non-empty at this point). Notice that there are several admissible hexadecimal representations for the same bitstring. Among them, the shortest one is “canonical”. It can be deterministically obtained by the above procedure. For example, 8A\texttt{8A} corresponds to binary string 10001010\texttt{10001010}, while 8A_\texttt{8A\_} and 8A0_\texttt{8A0\_} both correspond to 100010\texttt{100010}. An empty bitstring may be represented by either ’ ’, '8_\texttt{8\_}', '0_\texttt{0\_}', '_\texttt{\_}', or '00_\texttt{00\_}'.

1.0.3. Emphasizing that a string is a hexadecimal representation of a bitstring

Sometimes we need to emphasize that a string of hexadecimal digits (with or without a _\texttt{\_} at the end) is the hexadecimal representation of a bitstring. In such cases, we either prepend x\texttt{x} to the resulting string (e.g., x8A\texttt{x8A}), or prepend x{\texttt{x\{} and append }\texttt{\}} (e.g., x{2D9_}\texttt{x\{2D9\_\}}, which is 00101101100\texttt{00101101100}). This should not be confused with hexadecimal numbers, usually prepended by 0x\texttt{0x} (e.g., 0x2D9\texttt{0x2D9} or 0x2d9\texttt{0x2d9}, which is the integer 729).

1.0.4. Serializing a bitstring into a sequence of octets

When a bitstring needs to be represented as a sequence of 8-bit bytes (octets), which take values in integers 02550\ldots255, this is achieved essentially in the same fashion as above: we split the bitstring into groups of eight bits and interpret each group as the binary representation of an integer 02550\ldots255. If the length of the bitstring is not a multiple of eight, the bitstring is augmented by a binary 1\texttt{1} and up to seven binary 0\texttt{0}s before being split into groups. The fact that such a completion has been applied is usually reflected by a “completion tag” bit. For instance, 00101101100\texttt{00101101100} corresponds to the sequence of two octets (0x2d,0x90)(\texttt{0x2d}, \texttt{0x90}) (hexadecimal), or (45,144)(45,144) (decimal), along with a completion tag bit equal to 1\texttt{1} (meaning that the completion has been applied), which must be stored separately. In some cases, it is more convenient to assume the completion is enabled by default rather than store an additional completion tag bit separately. Under such conventions, 8n8n-bit strings are represented by n+1n+1 octets, with the last octet always equal to 0x80=128\texttt{0x80}=128.

1.1 TVM is a stack machine

First of all, TVM is a stack machine. This means that, instead of keeping values in some “variables” or “general-purpose registers”, they are kept in a (LIFO) stack, at least from the “low-level” (TVM) perspective.3 Most operations and user-defined functions take their arguments from the top of the stack, and replace them with their result. For example, the integer addition primitive (built-in operation) ADD\texttt{ADD} does not take any arguments describing which registers or immediate values should be added together and where the result should be stored. Instead, the two top values are taken from the stack, they are added together, and their sum is pushed into the stack in their place.

1.1.1. TVM values

The entities that can be stored in the TVM stack will be called TVM values, or simply values for brevity. They belong to one of several predefined value types. Each value belongs to exactly one value type. The values are always kept on the stack along with tags uniquely determining their types, and all built-in TVM operations (or primitives) only accept values of predefined types. For example, the integer addition primitive ADD\texttt{ADD} accepts only two integer values, and returns one integer value as a result. One cannot supply ADD\texttt{ADD} with two strings instead of two integers expecting it to concatenate these strings or to implicitly transform the strings into their decimal integer values; any attempt to do so will result in a run-time type-checking exception.

1.1.2. Static typing, dynamic typing, and run-time type checking

In some respects TVM performs a kind of dynamic typing using run-time type checking. However, this does not make the TVM code a “dynamically typed language” like PHP or Javascript, because all primitives accept values and return results of predefined (value) types, each value belongs to strictly one type, and values are never implicitly converted from one type to another. If, on the other hand, one compares the TVM code to the conventional microprocessor machine code, one sees that the TVM mechanism of value tagging prevents, for example, using the address of a string as a number—or, potentially even more disastrously, using a number as the address of a string—thus eliminating the possibility of all sorts of bugs and security vulnerabilities related to invalid memory accesses, usually leading to memory corruption and segmentation faults. This property is highly desirable for a VM used to execute smart contracts in a blockchain. In this respect, TVM’s insistence on tagging all values with their appropriate types, instead of reinterpreting the bit sequence in a register depending on the needs of the operation it is used in, is just an additional run-time type-safety mechanism. An alternative would be to somehow analyze the smart-contract code for type correctness and type safety before allowing its execution in the VM, or even before allowing it to be uploaded into the blockchain as the code of a smart contract. Such a static analysis of code for a Turing-complete machine appears to be a time-consuming and non-trivial problem (likely to be equivalent to the stopping problem for Turing machines), something we would rather avoid in a blockchain smart-contract context. One should bear in mind that one always can implement compilers from statically typed high-level smart-contract languages into the TVM code (and we do expect that most smart contracts for TON will be written in such languages), just as one can compile statically typed languages into conventional machine code (e.g., x86 architecture). If the compiler works correctly, the resulting machine code will never generate any run-time type-checking exceptions. All type tags attached to values processed by TVM will always have expected values and may be safely ignored during the analysis of the resulting TVM code, apart from the fact that the run-time generation and verification of these type tags by TVM will slightly slow down the execution of the TVM code.

1.1.3. Preliminary list of value types

A preliminary list of value types supported by TVM is as follows:
  • Integer — Signed 257-bit integers, representing integer numbers in the range 225622561-2^{256}\ldots2^{256}-1, as well as a special “not-a-number” value NaN\texttt{NaN}.
  • Cell — A TVM cell consists of at most 1023 bits of data, and of at most four references to other cells. All persistent data (including TVM code) in the TON Blockchain is represented as a collection of TVM cells (cf. [1], 2.5.14).
  • Tuple — An ordered collection of up to 255 components, having arbitrary value types, possibly distinct. May be used to represent non-persistent values of arbitrary algebraic data types.
  • Null — A type with exactly one value \bot, used for representing empty lists, empty branches of binary trees, absence of return value in some situations, and so on.
  • Slice — A TVM cell slice, or slice for short, is a contiguous “sub-cell” of an existing cell, containing some of its bits of data and some of its references. Essentially, a slice is a read-only view for a subcell of a cell. Slices are used for unpacking data previously stored (or serialized) in a cell or a tree of cells.
  • Builder — A TVM cell builder, or builder for short, is an “incomplete” cell that supports fast operations of appending bitstrings and cell references at its end. Builders are used for packing (or serializing) data from the top of the stack into new cells (e.g., before transferring them to persistent storage).
  • Continuation — Represents an “execution token” for TVM, which may be invoked (executed) later. As such, it generalizes function addresses (i.e., function pointers and references), subroutine return addresses, instruction pointer addresses, exception handler addresses, closures, partial applications, anonymous functions, and so on.
This list of value types is incomplete and may be extended in future revisions of TVM without breaking the old TVM code, due mostly to the fact that all originally defined primitives accept only values of types known to them and will fail (generate a type-checking exception) if invoked on values of new types. Furthermore, existing value types themselves can also be extended in the future: for example, 257-bit Integer might become 513-bit LongInteger, with originally defined arithmetic primitives failing if either of the arguments or the result does not fit into the original subtype Integer. Backward compatibility with respect to the introduction of new value types and extension of existing value types will be discussed in more detail later (cf. 5.1.4).

1.2 Categories of TVM instructions

TVM instructions, also called primitives and sometimes (built-in) operations, are the smallest operations atomically performed by TVM that can be present in the TVM code. They fall into several categories, depending on the types of values (cf. 1.1.3) they work on. The most important of these categories are:
  • Stack (manipulation) primitives — Rearrange data in the TVM stack, so that the other primitives and user-defined functions can later be called with correct arguments. Unlike most other primitives, they are polymorphic, i.e., work with values of arbitrary types.
  • Tuple (manipulation) primitives — Construct, modify, and decompose Tuples. Similarly to the stack primitives, they are polymorphic.
  • Constant or literal primitives — Push into the stack some “constant” or “literal” values embedded into the TVM code itself, thus providing arguments to the other primitives. They are somewhat similar to stack primitives, but are less generic because they work with values of specific types.
  • Arithmetic primitives — Perform the usual integer arithmetic operations on values of type Integer.
  • Cell (manipulation) primitives — Create new cells and store data in them (cell creation primitives) or read data from previously created cells (cell parsing primitives). Because all memory and persistent storage of TVM consists of cells, these cell manipulation primitives actually correspond to “memory access instructions” of other architectures. Cell creation primitives usually work with values of type Builder, while cell parsing primitives work with Slices.
  • Continuation and control flow primitives — Create and modify Continuations, as well as execute existing Continuations in different ways, including conditional and repeated execution.
  • Custom or application-specific primitives — Efficiently perform specific high-level actions required by the application (in our case, the TON Blockchain), such as computing hash functions, performing elliptic curve cryptography, sending new blockchain messages, creating new smart contracts, and so on. These primitives correspond to standard library functions rather than microprocessor instructions.

1.3 Control registers

While TVM is a stack machine, some rarely changed values needed in almost all functions are better passed in certain special registers, and not near the top of the stack. Otherwise, a prohibitive number of stack reordering operations would be required to manage all these values. To this end, the TVM model includes, apart from the stack, up to 16 special control registers, denoted by c0\texttt{c0} to c15\texttt{c15}, or c(0)\texttt{c}(0) to c(15)\texttt{c}(15). The original version of TVM makes use of only some of these registers; the rest may be supported later.

1.3.1. Values kept in control registers

The values kept in control registers are of the same types as those kept on the stack. However, some control registers accept only values of specific types, and any attempt to load a value of a different type will lead to an exception.

1.3.2. List of control registers

The original version of TVM defines and uses the following control registers:
  • c0\texttt{c0} — Contains the next continuation or return continuation (similar to the subroutine return address in conventional designs). This value must be a Continuation.
  • c1\texttt{c1} — Contains the alternative (return) continuation; this value must be a Continuation. It is used in some (experimental) control flow primitives, allowing TVM to define and call “subroutines with two exit points”.
  • c2\texttt{c2} — Contains the exception handler. This value is a Continuation, invoked whenever an exception is triggered.
  • c3\texttt{c3} — Contains the current dictionary, essentially a hashmap containing the code of all functions used in the program. For reasons explained later in 4.6, this value is also a Continuation, not a Cell as one might expect.
  • c4\texttt{c4} — Contains the root of persistent data, or simply the data. This value is a Cell. When the code of a smart contract is invoked, c4\texttt{c4} points to the root cell of its persistent data kept in the blockchain state. If the smart contract needs to modify this data, it changes c4\texttt{c4} before returning.
  • c5\texttt{c5} — Contains the output actions. It is also a Cell initialized by a reference to an empty cell, but its final value is considered one of the smart contract outputs. For instance, the SENDMSG\texttt{SENDMSG} primitive, specific for the TON Blockchain, simply inserts the message into a list stored in the output actions.
  • c7\texttt{c7} — Contains the root of temporary data. It is a Tuple, initialized by a reference to an empty Tuple before invoking the smart contract and discarded after its termination.4
More control registers may be defined in the future for specific TON Blockchain or high-level programming language purposes, if necessary.

1.4 Total state of TVM (SCCCG)

The total state of TVM consists of the following components:
  • Stack (cf. 1.1) — Contains zero or more values (cf. 1.1.1), each belonging to one of value types listed in 1.1.3.
  • Control registers c0\mathit{c0}c15\mathit{c15} — Contain some specific values as described in 1.3.2. (Only seven control registers are used in the current version.)
  • Current continuation cc\mathit{cc} — Contains the current continuation (i.e., the code that would be normally executed after the current primitive is completed). This component is similar to the instruction pointer register (ip\texttt{ip}) in other architectures.
  • Current codepage cp\mathit{cp} — A special signed 16-bit integer value that selects the way the next TVM opcode will be decoded. For example, future versions of TVM might use different codepages to add new opcodes while preserving backward compatibility.
  • Gas limits gas\mathit{gas} — Contains four signed 64-bit integers: the current gas limit glg_l, the maximal gas limit gmg_m, the remaining gas grg_r, and the gas credit gcg_c. Always 0glgm0\leq g_l\leq g_m, gc0g_c\geq0, and grgl+gcg_r\leq g_l+g_c; gcg_c is usually initialized by zero, grg_r is initialized by gl+gcg_l+g_c and gradually decreases as the TVM runs. When grg_r becomes negative or if the final value of grg_r is less than gcg_c, an out of gas exception is triggered.
Notice that there is no “return stack” containing the return addresses of all previously called but unfinished functions. Instead, only control register c0\texttt{c0} is used. The reason for this will be explained later in 4.1.9. Also notice that there are no general-purpose registers, because TVM is a stack machine (cf. 1.1). So the above list, which can be summarized as “stack, control, continuation, codepage, and gas” (SCCCG), similarly to the classical SECD machine state (“stack, environment, control, dump”), is indeed the total state of TVM.5

1.5 Integer arithmetic

All arithmetic primitives of TVM operate on several arguments of type Integer, taken from the top of the stack, and return their results, of the same type, into the stack. Recall that Integer represents all integer values in the range 2256x<2256-2^{256}\leq x<2^{256}, and additionally contains a special value NaN\texttt{NaN} (“not-a-number”). If one of the results does not fit into the supported range of integers—or if one of the arguments is a NaN\texttt{NaN}—then this result or all of the results are replaced by a NaN\texttt{NaN}, and (by default) an integer overflow exception is generated. However, special “quiet” versions of arithmetic operations will simply produce NaN\texttt{NaN}s and keep going. If these NaN\texttt{NaN}s end up being used in a “non-quiet” arithmetic operation, or in a non-arithmetic operation, an integer overflow exception will occur.

1.5.1. Absence of automatic conversion of integers

Notice that TVM Integers are “mathematical” integers, and not 257-bit strings interpreted differently depending on the primitive used, as is common for other machine code designs. For example, TVM has only one multiplication primitive MUL\texttt{MUL}, rather than two (MUL\texttt{MUL} for unsigned multiplication and IMUL\texttt{IMUL} for signed multiplication) as occurs, for example, in the popular x86 architecture.

1.5.2. Automatic overflow checks

Notice that all TVM arithmetic primitives perform overflow checks of the results. If a result does not fit into the Integer type, it is replaced by a NaN\texttt{NaN}, and (usually) an exception occurs. In particular, the result is not automatically reduced modulo 22562^{256} or 22572^{257}, as is common for most hardware machine code architectures.

1.5.3. Custom overflow checks

In addition to automatic overflow checks, TVM includes custom overflow checks, performed by primitives FITS\texttt{FITS} nn and UFITS\texttt{UFITS} nn, where 1n2561\leq n\leq256. These primitives check whether the value on (the top of) the stack is an integer xx in the range 2n1x<2n1-2^{n-1}\leq x<2^{n-1} or 0x<2n0\leq x<2^n, respectively, and replace the value with a NaN\texttt{NaN} and (optionally) generate an integer overflow exception if this is not the case. This greatly simplifies the implementation of arbitrary nn-bit integer types, signed or unsigned: the programmer or the compiler must insert appropriate FITS\texttt{FITS} or UFITS\texttt{UFITS} primitives either after each arithmetic operation (which is more reasonable, but requires more checks) or before storing computed values and returning them from functions. This is important for smart contracts, where unexpected integer overflows happen to be among the most common source of bugs.

1.5.4. Reduction modulo 2n2^n

TVM also has a primitive MODPOW2\texttt{MODPOW2} nn, which reduces the integer at the top of the stack modulo 2n2^n, with the result ranging from 00 to 2n12^n-1.

1.5.5. Integer is 257-bit, not 256-bit

One can understand now why TVM’s Integer is (signed) 257-bit, not 256-bit. The reason is that it is the smallest integer type containing both signed 256-bit integers and unsigned 256-bit integers, which does not require automatic reinterpreting of the same 256-bit string depending on the operation used (cf. 1.5.1).

1.5.6. Division and rounding

The most important division primitives are DIV\texttt{DIV}, MOD\texttt{MOD}, and DIVMOD\texttt{DIVMOD}. All of them take two numbers from the stack, xx and yy (yy is taken from the top of the stack, and xx is originally under it), compute the quotient qq and remainder rr of the division of xx by yy (i.e., two integers such that x=yq+rx=yq+r and r<y|r|<|y|), and return either qq, rr, or both of them. If yy is zero, then all of the expected results are replaced by NaN\texttt{NaN}s, and (usually) an integer overflow exception is generated. The implementation of division in TVM somewhat differs from most other implementations with regards to rounding. By default, these primitives round to -\infty, meaning that q=x/yq=\lfloor x/y\rfloor, and rr has the same sign as yy. (Most conventional implementations of division use “rounding to zero” instead, meaning that rr has the same sign as xx.) Apart from this “floor rounding”, two other rounding modes are available, called “ceiling rounding” (with q=x/yq=\lceil x/y\rceil, and rr and yy having opposite signs) and “nearest rounding” (with q=x/y+1/2q=\lfloor x/y+1/2\rfloor and ry/2|r|\leq|y|/2). These rounding modes are selected by using other division primitives, with letters C\texttt{C} and R\texttt{R} appended to their mnemonics. For example, DIVMODR\texttt{DIVMODR} computes both the quotient and the remainder using rounding to the nearest integer.

1.5.7. Combined multiply-divide, multiply-shift, and shift-divide operations

To simplify implementation of fixed-point arithmetic, TVM supports combined multiply-divide, multiply-shift, and shift-divide operations with double-length (i.e., 514-bit) intermediate product. For example, MULDIVMODR\texttt{MULDIVMODR} takes three integer arguments from the stack, aa, bb, and cc, first computes abab using a 514-bit intermediate result, and then divides abab by cc using rounding to the nearest integer. If cc is zero or if the quotient does not fit into Integer, either two NaN\texttt{NaN}s are returned, or an integer overflow exception is generated, depending on whether a quiet version of the operation has been used. Otherwise, both the quotient and the remainder are pushed into the stack.

2 The stack

This chapter contains a general discussion and comparison of register and stack machines, expanded further in Appendix C, and describes the two main classes of stack manipulation primitives employed by TVM: the basic and the compound stack manipulation primitives. An informal explanation of their sufficiency for all stack reordering required for correctly invoking other primitives and user-defined functions is also provided. Finally, the problem of efficiently implementing TVM stack manipulation primitives is discussed in 2.3.

2.1 Stack calling conventions

A stack machine, such as TVM, uses the stack—and especially the values near the top of the stack—to pass arguments to called functions and primitives (such as built-in arithmetic operations) and receive their results. This section discusses the TVM stack calling conventions, introduces some notation, and compares TVM stack calling conventions with those of certain register machines.

2.1.1. Notation for “stack registers”

Recall that a stack machine, as opposed to a more conventional register machine, lacks general-purpose registers. However, one can treat the values near the top of the stack as a kind of “stack registers”. We denote by s0\texttt{s0} or s(0)\texttt{s}(0) the value at the top of the stack, by s1\texttt{s1} or s(1)\texttt{s}(1) the value immediately under it, and so on. The total number of values in the stack is called its depth. If the depth of the stack is nn, then s(0)\texttt{s}(0), s(1)\texttt{s}(1), \ldots, s(n1)\texttt{s}(n-1) are well-defined, while s(n)\texttt{s}(n) and all subsequent s(i)\texttt{s}(i) with i>ni>n are not. Any attempt to use s(i)\texttt{s}(i) with ini\geq n should produce a stack underflow exception. A compiler, or a human programmer in “TVM code”, would use these “stack registers” to hold all declared variables and intermediate values, similarly to the way general-purpose registers are used on a register machine.

2.1.2. Pushing and popping values

When a value xx is pushed into a stack of depth nn, it becomes the new s0\texttt{s0}; at the same time, the old s0\texttt{s0} becomes the new s1\texttt{s1}, the old s1\texttt{s1}—the new s2\texttt{s2}, and so on. The depth of the resulting stack is n+1n+1. Similarly, when a value xx is popped from a stack of depth n1n\geq1, it is the old value of s0\texttt{s0} (i.e., the old value at the top of the stack). After this, it is removed from the stack, and the old s1\texttt{s1} becomes the new s0\texttt{s0} (the new value at the top of the stack), the old s2\texttt{s2} becomes the new s1\texttt{s1}, and so on. The depth of the resulting stack is n1n-1. If originally n=0n=0, then the stack is empty, and a value cannot be popped from it. If a primitive attempts to pop a value from an empty stack, a stack underflow exception occurs.

2.1.3. Notation for hypothetical general-purpose registers

In order to compare stack machines with sufficiently general register machines, we will denote the general-purpose registers of a register machine by r0\texttt{r0}, r1\texttt{r1}, and so on, or by r(0)\texttt{r}(0), r(1)\texttt{r}(1), \ldots, r(n1)\texttt{r}(n-1), where nn is the total number of registers. When we need a specific value of nn, we will use n=16n=16, corresponding to the very popular x86-64 architecture.

2.1.4. The top-of-stack register s0\texttt{s0} vs. the accumulator register r0\texttt{r0}

Some register machine architectures require one of the arguments for most arithmetic and logical operations to reside in a special register called the accumulator. In our comparison, we will assume that the accumulator is the general-purpose register r0\texttt{r0}; otherwise we could simply renumber the registers. In this respect, the accumulator is somewhat similar to the top-of-stack “register” s0\texttt{s0} of a stack machine, because virtually all operations of a stack machine both use s0\texttt{s0} as one of their arguments and return their result as s0\texttt{s0}.

2.1.5. Register calling conventions

When compiled for a register machine, high-level language functions usually receive their arguments in certain registers in a predefined order. If there are too many arguments, these functions take the remainder from the stack (yes, a register machine usually has a stack, too!). Some register calling conventions pass no arguments in registers at all, however, and only use the stack (for example, the original calling conventions used in implementations of Pascal and C, although modern implementations of C use some registers as well). For simplicity, we will assume that up to mnm\leq n function arguments are passed in registers, and that these registers are r0\texttt{r0}, r1\texttt{r1}, \ldots, r(m1)\texttt{r}(m-1), in that order (if some other registers are used, we can simply renumber them).6

2.1.6. Order of function arguments

If a function or primitive requires mm arguments x1x_1, \ldots, xmx_m, they are pushed by the caller into the stack in the same order, starting from x1x_1. Therefore, when the function or primitive is invoked, its first argument x1x_1 is in s(m1)\texttt{s}(m-1), its second argument x2x_2 is in s(m2)\texttt{s}(m-2), and so on. The last argument xmx_m is in s0\texttt{s0} (i.e., at the top of the stack). It is the called function or primitive’s responsibility to remove its arguments from the stack. In this respect the TVM stack calling conventions—obeyed, at least, by TVM primitives—match those of Pascal and Forth, and are the opposite of those of C (in which the arguments are pushed into the stack in the reverse order, and are removed by the caller after it regains control, not the callee). Of course, an implementation of a high-level language for TVM might choose some other calling conventions for its functions, different from the default ones. This might be useful for certain functions—for instance, if the total number of arguments depends on the value of the first argument, as happens for “variadic functions” such as scanf\texttt{scanf} and printf\texttt{printf}. In such cases, the first one or several arguments are better passed near the top of the stack, not somewhere at some unknown location deep in the stack.

2.1.7. Arguments to arithmetic primitives on register machines

On a stack machine, built-in arithmetic primitives (such as ADD\texttt{ADD} or DIVMOD\texttt{DIVMOD}) follow the same calling conventions as user-defined functions. In this respect, user-defined functions (for example, a function computing the square root of a number) might be considered as “extensions” or “custom upgrades” of the stack machine. This is one of the clearest advantages of stack machines (and of stack programming languages such as Forth) compared to register machines. In contrast, arithmetic instructions (built-in operations) on register machines usually get their parameters from general-purpose registers encoded in the full opcode. A binary operation, such as SUB\texttt{SUB}, thus requires two arguments, r(i)\texttt{r}(i) and r(j)\texttt{r}(j), with ii and jj specified by the instruction. A register r(k)\texttt{r}(k) for storing the result also must be specified. Arithmetic operations can take several possible forms, depending on whether ii, jj, and kk are allowed to take arbitrary values:
  • Three-address form — Allows the programmer to arbitrarily choose not only the two source registers r(i)\texttt{r}(i) and r(j)\texttt{r}(j), but also a separate destination register r(k)\texttt{r}(k). This form is common for most RISC processors, and for the XMM and AVX SIMD instruction sets in the x86-64 architecture.
  • Two-address form — Uses one of the two operand registers (usually r(i)\texttt{r}(i)) to store the result of an operation, so that k=ik=i is never indicated explicitly. Only ii and jj are encoded inside the instruction. This is the most common form of arithmetic operations on register machines, and is quite popular on microprocessors (including the x86 family).
  • One-address form — Always takes one of the arguments from the accumulator r0\texttt{r0}, and stores the result in r0\texttt{r0} as well; then i=k=0i=k=0, and only jj needs to be specified by the instruction. This form is used by some simpler microprocessors (such as Intel 8080).
Note that this flexibility is available only for built-in operations, but not for user-defined functions. In this respect, register machines are not as easily “upgradable” as stack machines.7

2.1.8. Return values of functions

In stack machines such as TVM, when a function or primitive needs to return a result value, it simply pushes it into the stack (from which all arguments to the function have already been removed). Therefore, the caller will be able to access the result value through the top-of-stack “register” s0\texttt{s0}. This is in complete accordance with Forth calling conventions, but differs slightly from Pascal and C calling conventions, where the accumulator register r0\texttt{r0} is normally used for the return value.

2.1.9. Returning several values

Some functions might want to return several values y1y_1, \ldots, yky_k, with kk not necessarily equal to one. In these cases, the kk return values are pushed into the stack in their natural order, starting from y1y_1. For example, the “divide with remainder” primitive DIVMOD\texttt{DIVMOD} needs to return two values, the quotient qq and the remainder rr. Therefore, DIVMOD\texttt{DIVMOD} pushes qq and rr into the stack, in that order, so that the quotient is available thereafter at s1\texttt{s1} and the remainder at s0\texttt{s0}. The net effect of DIVMOD\texttt{DIVMOD} is to divide the original value of s1\texttt{s1} by the original value of s0\texttt{s0}, and return the quotient in s1\texttt{s1} and the remainder in s0\texttt{s0}. In this particular case the depth of the stack and the values of all other “stack registers” remain unchanged, because DIVMOD\texttt{DIVMOD} takes two arguments and returns two results. In general, the values of other “stack registers” that lie in the stack below the arguments passed and the values returned are shifted according to the change of the depth of the stack. In principle, some primitives and user-defined functions might return a variable number of result values. In this respect, the remarks above about variadic functions (cf. 2.1.6) apply: the total number of result values and their types should be determined by the values near the top of the stack. (For example, one might push the return values y1y_1, \ldots, yky_k, and then push their total number kk as an integer. The caller would then determine the total number of returned values by inspecting s0\texttt{s0}.) In this respect TVM, again, faithfully observes Forth calling conventions.

2.1.10. Stack notation

When a stack of depth nn contains values z1z_1, \ldots, znz_n, in that order, with z1z_1 the deepest element and znz_n the top of the stack, the contents of the stack are often represented by a list z1z_1 z2z_2 \ldots znz_n, in that order. When a primitive transforms the original stack state SS' into a new state SS'', this is often written as SS'SS''; this is the so-called stack notation. For example, the action of the division primitive DIV\texttt{DIV} can be described by SS xx yySS x/y\lfloor x/y\rfloor, where SS is any list of values. This is usually abbreviated as xx yyx/y\lfloor x/y\rfloor, tacitly assuming that all other values deeper in the stack remain intact. Alternatively, one can describe DIV\texttt{DIV} as a primitive that runs on a stack SS' of depth n2n\geq2, divides s1\texttt{s1} by s0\texttt{s0}, and returns the floor-rounded quotient as s0\texttt{s0} of the new stack SS'' of depth n1n-1. The new value of s(i)\texttt{s}(i) equals the old value of s(i+1)\texttt{s}(i+1) for 1i<n11\leq i<n-1. These descriptions are equivalent, but saying that DIV\texttt{DIV} transforms xx yy into x/y\lfloor x/y\rfloor, or \ldots xx yy into \ldots x/y\lfloor x/y\rfloor, is more concise. The stack notation is extensively used throughout Appendix A, where all currently defined TVM primitives are listed.

2.1.11. Explicitly defining the number of arguments to a function

Stack machines usually pass the current stack in its entirety to the invoked primitive or function. That primitive or function accesses only the several values near the top of the stack that represent its arguments, and pushes the return values in their place, by convention leaving all deeper values intact. Then the resulting stack, again in its entirety, is returned to the caller. Most TVM primitives behave in this way, and we expect most user-defined functions to be implemented under such conventions. However, TVM provides mechanisms to specify how many arguments must be passed to a called function (cf. 4.1.10). When these mechanisms are employed, the specified number of values are moved from the caller’s stack into the (usually initially empty) stack of the called function, while deeper values remain in the caller’s stack and are inaccessible to the callee. The caller can also specify how many return values it expects from the called function. Such argument-checking mechanisms might be useful, for example, for a library function that calls user-provided functions passed as arguments to it.

2.2 Stack manipulation primitives

A stack machine, such as TVM, employs a lot of stack manipulation primitives to rearrange arguments to other primitives and user-defined functions, so that they become located near the top of the stack in correct order. This section discusses which stack manipulation primitives are necessary and sufficient for achieving this goal, and which of them are used by TVM. Some examples of code using these primitives can be found in Appendix C.

2.2.1. Basic stack manipulation primitives

The most important stack manipulation primitives used by TVM are the following:
  • Top-of-stack exchange operation: XCHG s0,s(i)\texttt{XCHG s0,s}(i) or XCHG s(i)\texttt{XCHG s}(i) — Exchanges values of s0\texttt{s0} and s(i)\texttt{s}(i). When i=1i=1, operation XCHG s1\texttt{XCHG s1} is traditionally denoted by SWAP\texttt{SWAP}. When i=0i=0, this is a NOP\texttt{NOP} (an operation that does nothing, at least if the stack is non-empty).
  • Arbitrary exchange operation: XCHG s(i),s(j)\texttt{XCHG s}(i)\texttt{,s}(j) — Exchanges values of s(i)\texttt{s}(i) and s(j)\texttt{s}(j). Notice that this operation is not strictly necessary, because it can be simulated by three top-of-stack exchanges: XCHG s(i)\texttt{XCHG s}(i); XCHG s(j)\texttt{XCHG s}(j); XCHG s(i)\texttt{XCHG s}(i). However, it is useful to have arbitrary exchanges as primitives, because they are required quite often.
  • Push operation: PUSH s(i)\texttt{PUSH s}(i) — Pushes a copy of the (old) value of s(i)\texttt{s}(i) into the stack. Traditionally, PUSH s0\texttt{PUSH s0} is also denoted by DUP\texttt{DUP} (it duplicates the value at the top of the stack), and PUSH s1\texttt{PUSH s1} by OVER\texttt{OVER}.
  • Pop operation: POP s(i)\texttt{POP s}(i) — Removes the top-of-stack value and puts it into the (new) s(i1)\texttt{s}(i-1), or the old s(i)\texttt{s}(i). Traditionally, POP s0\texttt{POP s0} is also denoted by DROP\texttt{DROP} (it simply drops the top-of-stack value), and POP s1\texttt{POP s1} by NIP\texttt{NIP}.
Some other “unsystematic” stack manipulation operations might be also defined (e.g., ROT\texttt{ROT}, with stack notation aa bb ccbb cc aa). While such operations are defined in stack languages like Forth (where DUP\texttt{DUP}, DROP\texttt{DROP}, OVER\texttt{OVER}, NIP\texttt{NIP} and SWAP\texttt{SWAP} are also present), they are not strictly necessary because the basic stack manipulation primitives listed above suffice to rearrange stack registers to allow any arithmetic primitives and user-defined functions to be invoked correctly.

2.2.2. Basic stack manipulation primitives suffice

A compiler or a human TVM-code programmer might use the basic stack primitives as follows. Suppose that the function or primitive to be invoked is to be passed, say, three arguments xx, yy, and zz, currently located in stack registers s(i)\texttt{s}(i), s(j)\texttt{s}(j), and s(k)\texttt{s}(k). In this circumstance, the compiler (or programmer) might issue operation PUSH s(i)\texttt{PUSH s}(i) (if a copy of xx is needed after the call to this primitive) or XCHG s(i)\texttt{XCHG s}(i) (if it will not be needed afterwards) to put the first argument xx into the top of the stack. Then, the compiler (or programmer) could use either PUSH s(j)\texttt{PUSH s}(j') or XCHG s(j)\texttt{XCHG s}(j'), where j=jj'=j or j+1j+1, to put yy into the new top of the stack.8 Proceeding in this manner, we see that we can put the original values of xx, yy, and zz—or their copies, if needed—into locations s2\texttt{s2}, s1\texttt{s1}, and s0\texttt{s0}, using a sequence of push and exchange operations (cf. 2.2.4 and 2.2.5 for a more detailed explanation). In order to generate this sequence, the compiler will need to know only the three values ii, jj and kk, describing the old locations of variables or temporary values in question, and some flags describing whether each value will be needed thereafter or is needed only for this primitive or function call. The locations of other variables and temporary values will be affected in the process, but a compiler (or a human programmer) can easily track their new locations. Similarly, if the results returned from a function need to be discarded or moved to other stack registers, a suitable sequence of exchange and pop operations will do the job. In the typical case of one return value in s0\texttt{s0}, this is achieved either by an XCHG s(i)\texttt{XCHG s}(i) or a POP s(i)\texttt{POP s}(i) (in most cases, a DROP\texttt{DROP}) operation.9 Rearranging the result value or values before returning from a function is essentially the same problem as arranging arguments for a function call, and is achieved similarly.

2.2.3. Compound stack manipulation primitives

In order to improve the density of the TVM code and simplify development of compilers, compound stack manipulation primitives may be defined, each combining up to four exchange and push or exchange and pop basic primitives. Such compound stack operations might include, for example:
  • XCHG2 s(i),s(j)\texttt{XCHG2 s}(i)\texttt{,s}(j) — Equivalent to XCHG s1,s(i)\texttt{XCHG s1,s}(i); XCHG s(j)\texttt{XCHG s}(j).
  • PUSH2 s(i),s(j)\texttt{PUSH2 s}(i)\texttt{,s}(j) — Equivalent to PUSH s(i)\texttt{PUSH s}(i); PUSH s(j+1)\texttt{PUSH s}(j+1).
  • XCPU s(i),s(j)\texttt{XCPU s}(i)\texttt{,s}(j) — Equivalent to XCHG s(i)\texttt{XCHG s}(i); PUSH s(j)\texttt{PUSH s}(j).
  • PUXC s(i),s(j)\texttt{PUXC s}(i)\texttt{,s}(j) — Equivalent to PUSH s(i)\texttt{PUSH s}(i); SWAP\texttt{SWAP}; XCHG s(j+1)\texttt{XCHG s}(j+1). When jij\neq i and j0j\neq0, it is also equivalent to XCHG s(j)\texttt{XCHG s}(j); PUSH s(i)\texttt{PUSH s}(i); SWAP\texttt{SWAP}.
  • XCHG3 s(i),s(j),s(k)\texttt{XCHG3 s}(i)\texttt{,s}(j)\texttt{,s}(k) — Equivalent to XCHG s2,s(i)\texttt{XCHG s2,s}(i); XCHG s1,s(j)\texttt{XCHG s1,s}(j); XCHG s(k)\texttt{XCHG s}(k).
  • PUSH3 s(i),s(j),s(k)\texttt{PUSH3 s}(i)\texttt{,s}(j)\texttt{,s}(k) — Equivalent to PUSH s(i)\texttt{PUSH s}(i); PUSH s(j+1)\texttt{PUSH s}(j+1); PUSH s(k+2)\texttt{PUSH s}(k+2).
Of course, such operations make sense only if they admit a more compact encoding than the equivalent sequence of basic operations. For example, if all top-of-stack exchanges, XCHG s1,s(i)\texttt{XCHG s1,s}(i) exchanges, and push and pop operations admit one-byte encodings, the only compound stack operations suggested above that might merit inclusion in the set of stack manipulation primitives are PUXC\texttt{PUXC}, XCHG3\texttt{XCHG3}, and PUSH3\texttt{PUSH3}. These compound stack operations essentially augment other primitives (instructions) in the code with the “true” locations of their operands, somewhat similarly to what happens with two-address or three-address register machine code. However, instead of encoding these locations inside the opcode of the arithmetic or another instruction, as is customary for register machines, we indicate these locations in a preceding compound stack manipulation operation. As already described in 2.1.7, the advantage of such an approach is that user-defined functions (or rarely used specific primitives added in a future version of TVM) can benefit from it as well (cf. C.3 for a more detailed discussion with examples).

2.2.4. Mnemonics of compound stack operations

The mnemonics of compound stack operations, some examples of which have been provided in 2.2.3, are created as follows. The γ2\gamma\geq2 formal arguments s(i1)\texttt{s}(i_1), \ldots, s(iγ)\texttt{s}(i_\gamma) to such an operation OO represent the values in the original stack that will end up in s(γ1)\texttt{s}(\gamma-1), \ldots, s0\texttt{s0} after the execution of this compound operation, at least if all iνi_\nu, 1νγ1\leq\nu\leq\gamma, are distinct and at least γ\gamma. The mnemonic itself of the operation OO is a sequence of γ\gamma two-letter strings PU\texttt{PU} and XC\texttt{XC}, with PU\texttt{PU} meaning that the corresponding argument is to be PUshed (i.e., a copy is to be created), and XC\texttt{XC} meaning that the value is to be eXChanged (i.e., no other copy of the original value is created). Sequences of several PU\texttt{PU} or XC\texttt{XC} strings may be abbreviated to one PU\texttt{PU} or XC\texttt{XC} followed by the number of copies. (For instance, we write PUXC2PU\texttt{PUXC2PU} instead of PUXCXCPU\texttt{PUXCXCPU}.) As an exception, if a mnemonic would consist of only PU\texttt{PU} or only XC\texttt{XC} strings, so that the compound operation is equivalent to a sequence of mm PUSHes or eXCHanGes, the notation PUSHm\texttt{PUSH}m or XCHGm\texttt{XCHG}m is used instead of PUm\texttt{PU}m or XCm\texttt{XC}m.

2.2.5. Semantics of compound stack operations

Each compound γ\gamma-ary operation OO s(i1)\texttt{s}(i_1),\ldots,s(iγ)\texttt{s}(i_\gamma) is translated into an equivalent sequence of basic stack operations by induction in γ\gamma as follows:
  • As a base of induction, if γ=0\gamma=0, the only nullary compound stack operation corresponds to an empty sequence of basic stack operations.
  • Equivalently, we might begin the induction from γ=1\gamma=1. Then PU s(i)\texttt{PU s}(i) corresponds to the sequence consisting of one basic operation PUSH s(i)\texttt{PUSH s}(i), and XC s(i)\texttt{XC s}(i) corresponds to the one-element sequence consisting of XCHG s(i)\texttt{XCHG s}(i).
  • For γ1\gamma\geq1 (or for γ2\gamma\geq2, if we use γ=1\gamma=1 as induction base), there are two subcases:
    1. OO s(i1)\texttt{s}(i_1),\ldots,s(iγ)\texttt{s}(i_\gamma), with O=XCOO=\texttt{XC}O', where OO' is a compound operation of arity γ1\gamma-1 (i.e., the mnemonic of OO' consists of γ1\gamma-1 strings XC\texttt{XC} and PU\texttt{PU}). Let α\alpha be the total quantity of PU\texttt{PU}shes in OO, and β\beta be that of eXC\texttt{XC}hanges, so that α+β=γ\alpha+\beta=\gamma. Then the original operation is translated into XCHG s(β1),s(i1)\texttt{XCHG s}(\beta-1)\texttt{,s}(i_1), followed by the translation of OO' s(i2)\texttt{s}(i_2),\ldots,s(iγ)\texttt{s}(i_\gamma), defined by the induction hypothesis.
    2. OO s(i1)\texttt{s}(i_1),\ldots,s(iγ)\texttt{s}(i_\gamma), with O=PUOO=\texttt{PU}O', where OO' is a compound operation of arity γ1\gamma-1. Then the original operation is translated into PUSH s(i1)\texttt{PUSH s}(i_1); XCHG s(β)\texttt{XCHG s}(\beta), followed by the translation of OO' s(i2+1)\texttt{s}(i_2+1),\ldots,s(iγ+1)\texttt{s}(i_\gamma+1), defined by the induction hypothesis.10

2.2.6. Stack manipulation instructions are polymorphic

Notice that the stack manipulation instructions are almost the only “polymorphic” primitives in TVM—i.e., they work with values of arbitrary types (including the value types that will appear only in future revisions of TVM). For example, SWAP\texttt{SWAP} always interchanges the two top values of the stack, even if one of them is an integer and the other is a cell. Almost all other instructions, especially the data processing instructions (including arithmetic instructions), require each of their arguments to be of some fixed type (possibly different for different arguments).

2.3 Efficiency of stack manipulation primitives

Stack manipulation primitives employed by a stack machine, such as TVM, have to be implemented very efficiently, because they constitute more than half of all the instructions used in a typical program. In fact, TVM performs all these instructions in a (small) constant time, regardless of the values involved (even if they represent very large integers or very large trees of cells).

2.3.1. Implementation of stack manipulation primitives: using references for operations instead of objects

The efficiency of TVM’s implementation of stack manipulation primitives results from the fact that a typical TVM implementation keeps in the stack not the value objects themselves, but only the references (pointers) to such objects. Therefore, a SWAP\texttt{SWAP} instruction only needs to interchange the references at s0\texttt{s0} and s1\texttt{s1}, not the actual objects they refer to.

2.3.2. Efficient implementation of DUP\texttt{DUP} and PUSH\texttt{PUSH} instructions using copy-on-write

Furthermore, a DUP\texttt{DUP} (or, more generally, PUSH s(i)\texttt{PUSH s}(i)) instruction, which appears to make a copy of a potentially large object, also works in small constant time, because it uses a copy-on-write technique of delayed copying: it copies only the reference instead of the object itself, but increases the “reference counter” inside the object, thus sharing the object between the two references. If an attempt to modify an object with a reference counter greater than one is detected, a separate copy of the object in question is made first (incurring a certain “non-uniqueness penalty” or “copying penalty” for the data manipulation instruction that triggered the creation of a new copy).

2.3.3. Garbage collecting and reference counting

When the reference counter of a TVM object becomes zero (for example, because the last reference to such an object has been consumed by a DROP\texttt{DROP} operation or an arithmetic instruction), it is immediately freed. Because cyclic references are impossible in TVM data structures, this method of reference counting provides a fast and convenient way of freeing unused objects, replacing slow and unpredictable garbage collectors.

2.3.4. Transparency of the implementation: Stack values are “values”, not “references”

Regardless of the implementation details just discussed, all stack values are really “values”, not “references”, from the perspective of the TVM programmer, similarly to the values of all types in functional programming languages. Any attempt to modify an existing object referred to from any other objects or stack locations will result in a transparent replacement of this object by its perfect copy before the modification is actually performed. In other words, the programmer should always act as if the objects themselves were directly manipulated by stack, arithmetic, and other data transformation primitives, and treat the previous discussion only as an explanation of the high efficiency of the stack manipulation primitives.

2.3.5. Absence of circular references

One might attempt to create a circular reference between two cells, AA and BB, as follows: first create AA and write some data into it; then create BB and write some data into it, along with a reference to previously constructed cell AA; finally, add a reference to BB into AA. While it may seem that after this sequence of operations we obtain a cell AA, which refers to BB, which in turn refers to AA, this is not the case. In fact, we obtain a new cell AA', which contains a copy of the data originally stored into cell AA along with a reference to cell BB, which contains a reference to (the original) cell AA. In this way the transparent copy-on-write mechanism and the “everything is a value” paradigm enable us to create new cells using only previously constructed cells, thus forbidding the appearance of circular references. This property also applies to all other data structures: for instance, the absence of circular references enables TVM to use reference counting to immediately free unused memory instead of relying on garbage collectors. Similarly, this property is crucial for storing data in the TON Blockchain.

3 Cells, memory, and persistent storage

This chapter briefly describes TVM cells, used to represent all data structures inside the TVM memory and its persistent storage, and the basic operations used to create cells, write (or serialize) data into them, and read (or deserialize) data from them.

3.1 Generalities on cells

This section presents a classification and general descriptions of cell types.

3.1.1. TVM memory and persistent storage consist of cells

Recall that the TVM memory and persistent storage consist of (TVM) cells. Each cell contains up to 1023 bits of data and up to four references to other cells.11 Circular references are forbidden and cannot be created by means of TVM (cf. 2.3.5). In this way, all cells kept in TVM memory and persistent storage constitute a directed acyclic graph (DAG).

3.1.2. Ordinary and exotic cells

Apart from the data and references, a cell has a cell type, encoded by an integer 1255-1\ldots255. A cell of type 1-1 is called ordinary; such cells do not require any special processing. Cells of other types are called exotic, and may be loaded—automatically replaced by other cells when an attempt to deserialize them (i.e., to convert them into a Slice by a CTOS\texttt{CTOS} instruction) is made. They may also exhibit a non-trivial behavior when their hashes are computed. The most common use for exotic cells is to represent some other cells—for instance, cells present in an external library, or pruned from the original tree of cells when a Merkle proof has been created. The type of an exotic cell is stored as the first eight data bits of its data. If an exotic cell has less than eight data bits, it is invalid.

3.1.3. The level of a cell

Every cell cc has another attribute Lvl(c)\text{Lvl}(c) called its (de Brujn) level, which currently takes integer values in the range 030\ldots3. The level of an ordinary cell is always equal to the maximum of the levels of all its children cic_i: Lvl(c)=max1irLvl(ci)(1)\text{Lvl}(c)=\max_{1\leq i\leq r}\text{Lvl}(c_i) \tag{1} for an ordinary cell cc containing rr references to cells c1c_1, \ldots, crc_r. If r=0r=0, Lvl(c)=0\text{Lvl}(c)=0. Exotic cells may have different rules for setting their level. A cell’s level affects the number of higher hashes it has. More precisely, a level ll cell has ll higher hashes Hash1(c)\text{Hash}_1(c), \ldots, Hashl(c)\text{Hash}_l(c) in addition to its representation hash Hash(c)=Hash(c)\text{Hash}(c)=\text{Hash}_\infty(c). Cells of non-zero level appear inside Merkle proofs and Merkle updates, after some branches of the tree of cells representing a value of an abstract data type are pruned.

3.1.4. Standard cell representation

When a cell needs to be transferred by a network protocol or stored in a disk file, it must be serialized. The standard representation CellRepr(c)=CellRepr(c)\text{CellRepr}(c)=\text{CellRepr}_\infty(c) of a cell cc as an octet (byte) sequence is constructed as follows:
  1. Two descriptor bytes d1d_1 and d2d_2 are serialized first. Byte d1d_1 equals r+8s+32lr+8s+32l, where 0r40\leq r\leq 4 is the quantity of cell references contained in the cell, 0l30\leq l\leq 3 is the level of the cell, and 0s10\leq s\leq 1 is 11 for exotic cells and 00 for ordinary cells. Byte d2d_2 equals b/8+b/8\lfloor b/8\rfloor+\lceil b/8\rceil, where 0b10230\leq b\leq 1023 is the quantity of data bits in cc.
  2. Then the data bits are serialized as b/8\lceil b/8\rceil 8-bit octets (bytes). If bb is not a multiple of eight, a binary 1\texttt{1} and up to six binary 0\texttt{0}s are appended to the data bits. After that, the data is split into b/8\lceil b/8\rceil eight-bit groups, and each group is interpreted as an unsigned big-endian integer 02550\ldots 255 and stored into an octet.
  3. Finally, each of the rr cell references is represented by 32 bytes containing the 256-bit representation hash Hash(ci)\text{Hash}(c_i), explained below in 3.1.5, of the cell cic_i referred to.
In this way, 2+b/8+32r2+\lceil b/8\rceil+32r bytes of CellRepr(c)\text{CellRepr}(c) are obtained.

3.1.5. The representation hash of a cell

The 256-bit representation hash or simply hash Hash(c)\text{Hash}(c) of a cell cc is recursively defined as the SHA-256 of the standard representation of the cell cc: Hash(c):=Sha256(CellRepr(c))(2)\text{Hash}(c):=\text{Sha256}\bigl(\text{CellRepr}(c)\bigr) \tag{2} Notice that cyclic cell references are not allowed and cannot be created by means of the TVM (cf. 2.3.5), so this recursion always ends, and the representation hash of any cell is well-defined.

3.1.6. The higher hashes of a cell

Recall that a cell cc of level ll has ll higher hashes Hashi(c)\text{Hash}_i(c), 1il1\leq i\leq l, as well. Exotic cells have their own rules for computing their higher hashes. Higher hashes Hashi(c)\text{Hash}_i(c) of an ordinary cell cc are computed similarly to its representation hash, but using the higher hashes Hashi(cj)\text{Hash}_i(c_j) of its children cjc_j instead of their representation hashes Hash(cj)\text{Hash}(c_j). By convention, we set Hash(c):=Hash(c)\text{Hash}_\infty(c):=\text{Hash}(c), and Hashi(c):=Hash(c)=Hash(c)\text{Hash}_i(c):=\text{Hash}_\infty(c)=\text{Hash}(c) for all i>li>l.12

3.1.7. Types of exotic cells

TVM currently supports the following cell types:
  • Type 1-1: Ordinary cell — Contains up to 1023 bits of data and up to four cell references.
  • Type 1: Pruned branch cell cc — May have any level 1l31\leq l\leq 3. It contains exactly 8+256l8+256l data bits: first an 8-bit integer equal to 1 (representing the cell’s type), then its ll higher hashes Hash1(c)\text{Hash}_1(c), \ldots, Hashl(c)\text{Hash}_l(c). The level ll of a pruned branch cell may be called its de Brujn index, because it determines the outer Merkle proof or Merkle update during the construction of which the branch has been pruned. An attempt to load a pruned branch cell usually leads to an exception.
  • Type 2: Library reference cell — Always has level 0, and contains 8+2568+256 data bits, including its 8-bit type integer 2 and the representation hash Hash(c)\text{Hash}(c') of the library cell being referred to. When loaded, a library reference cell may be transparently replaced by the cell it refers to, if found in the current library context.
  • Type 3: Merkle proof cell cc — Has exactly one reference c1c_1 and level 0l30\leq l\leq 3, which must be one less than the level of its only child c1c_1: Lvl(c)=max(Lvl(c1)1,0)(3)\text{Lvl}(c)=\max(\text{Lvl}(c_1)-1,0) \tag{3} The 8+2568+256 data bits of a Merkle proof cell contain its 8-bit type integer 3, followed by Hash1(c1)\text{Hash}_1(c_1) (assumed to be equal to Hash(c1)\text{Hash}(c_1) if Lvl(c1)=0\text{Lvl}(c_1)=0). The higher hashes Hashi(c)\text{Hash}_i(c) of cc are computed similarly to the higher hashes of an ordinary cell, but with Hashi+1(c1)\text{Hash}_{i+1}(c_1) used instead of Hashi(c1)\text{Hash}_i(c_1). When loaded, a Merkle proof cell is replaced by c1c_1.
  • Type 4: Merkle update cell cc — Has two children c1c_1 and c2c_2. Its level 0l30\leq l\leq 3 is given by Lvl(c)=max(Lvl(c1)1,Lvl(c2)1,0)(4)\text{Lvl}(c)=\max(\text{Lvl}(c_1)-1,\text{Lvl}(c_2)-1,0) \tag{4} A Merkle update behaves like a Merkle proof for both c1c_1 and c2c_2, and contains 8+256+2568+256+256 data bits with Hash1(c1)\text{Hash}_1(c_1) and Hash1(c2)\text{Hash}_1(c_2). However, an extra requirement is that all pruned branch cells cc' that are descendants of c2c_2 and are bound by cc must also be descendants of c1c_1.13 When a Merkle update cell is loaded, it is replaced by c2c_2.

3.1.8. All values of algebraic data types are trees of cells

Arbitrary values of arbitrary algebraic data types (e.g., all types used in functional programming languages) can be serialized into trees of cells (of level 0), and such representations are used for representing such values within TVM. The copy-on-write mechanism (cf. 2.3.2) allows TVM to identify cells containing the same data and references, and to keep only one copy of such cells. This actually transforms a tree of cells into a directed acyclic graph (with the additional property that all its vertices be accessible from a marked vertex called the “root”). However, this is a storage optimization rather than an essential property of TVM. From the perspective of a TVM code programmer, one should think of TVM data structures as trees of cells.

3.1.9. TVM code is a tree of cells

The TVM code itself is also represented by a tree of cells. Indeed, TVM code is simply a value of some complex algebraic data type, and as such, it can be serialized into a tree of cells. The exact way in which the TVM code (e.g., TVM assembly code) is transformed into a tree of cells is explained later (cf. 4.1.4 and 5.2), in sections discussing control flow instructions, continuations, and TVM instruction encoding.

3.1.10. “Everything is a bag of cells” paradigm

As described in [1, 2.5.14], all the data used by the TON Blockchain, including the blocks themselves and the blockchain state, can be represented—and are represented—as collections, or “bags”, of cells. We see that TVM’s structure of data (cf. 3.1.8) and code (cf. 3.1.9) nicely fits into this “everything is a bag of cells” paradigm. In this way, TVM can naturally be used to execute smart contracts in the TON Blockchain, and the TON Blockchain can be used to store the code and persistent data of these smart contracts between invocations of TVM. (Of course, both TVM and the TON Blockchain have been designed so that this would become possible.)

3.2 Data manipulation instructions and cells

The next large group of TVM instructions consists of data manipulation instructions, also known as cell manipulation instructions or simply cell instructions. They correspond to memory access instructions of other architectures.

3.2.1. Classes of cell manipulation instructions

The TVM cell instructions are naturally subdivided into two principal classes:
  • Cell creation instructions or serialization instructions, used to construct new cells from values previously kept in the stack and previously constructed cells.
  • Cell parsing instructions or deserialization instructions, used to extract data previously stored into cells by cell creation instructions.
Additionally, there are exotic cell instructions used to create and inspect exotic cells (cf. 3.1.2), which in particular are used to represent pruned branches of Merkle proofs and Merkle proofs themselves.

3.2.2. Builder and Slice values

Cell creation instructions usually work with Builder values, which can be kept only in the stack (cf. 1.1.3). Such values represent partially constructed cells, for which fast operations for appending bitstrings, integers, other cells, and references to other cells can be defined. Similarly, cell parsing instructions make heavy use of Slice values, which represent either the remainder of a partially parsed cell, or a value (subcell) residing inside such a cell and extracted from it by a parsing instruction.

3.2.3. Builder and Slice values exist only as stack values

Notice that Builder and Slice objects appear only as values in a TVM stack. They cannot be stored in “memory” (i.e., trees of cells) or “persistent storage” (which is also a bag of cells). In this sense, there are far more Cell objects than Builder or Slice objects in a TVM environment, but, somewhat paradoxically, a TVM program sees Builder and Slice objects in its stack more often than Cells. In fact, a TVM program does not have much use for Cell values, because they are immutable and opaque; all cell manipulation primitives require that a Cell value be transformed into either a Builder or a Slice first, before it can be modified or inspected.

3.2.4. TVM has no separate Bitstring value type

Notice that TVM offers no separate bitstring value type. Instead, bitstrings are represented by Slices that happen to have no references at all, but can still contain up to 1023 data bits.

3.2.5. Cells and cell primitives are bit-oriented, not byte-oriented

An important point is that TVM regards data kept in cells as sequences (strings, streams) of (up to 1023) bits, not of bytes. In other words, TVM is a bit-oriented machine, not a byte-oriented machine. If necessary, an application is free to use, say, 21-bit integer fields inside records serialized into TVM cells, thus using fewer persistent storage bytes to represent the same data.

3.2.6. Taxonomy of cell creation (serialization) primitives

Cell creation primitives usually accept a Builder argument and an argument representing the value to be serialized. Additional arguments controlling some aspects of the serialization process (e.g., how many bits should be used for serialization) can be also provided, either in the stack or as an immediate value inside the instruction. The result of a cell creation primitive is usually another Builder, representing the concatenation of the original builder and the serialization of the value provided. Therefore, one can suggest a classification of cell serialization primitives according to the answers to the following questions:
  • Which is the type of values being serialized?
  • How many bits are used for serialization? If this is a variable number, does it come from the stack, or from the instruction itself?
  • What happens if the value does not fit into the prescribed number of bits? Is an exception generated, or is a success flag equal to zero silently returned in the top of stack?
  • What happens if there is insufficient space left in the Builder? Is an exception generated, or is a zero success flag returned along with the unmodified original Builder?
The mnemonics of cell serialization primitives usually begin with ST\texttt{ST}. Subsequent letters describe the following attributes:
  • The type of values being serialized and the serialization format (e.g., I\texttt{I} for signed integers, U\texttt{U} for unsigned integers).
  • The source of the field width in bits to be used (e.g., X\texttt{X} for integer serialization instructions means that the bit width nn is supplied in the stack; otherwise it has to be embedded into the instruction as an immediate value).
  • The action to be performed if the operation cannot be completed (by default, an exception is generated; “quiet” versions of serialization instructions are marked by a Q\texttt{Q} letter in their mnemonics).
This classification scheme is used to create a more complete taxonomy of cell serialization primitives, which can be found in A.7.1.

3.2.7. Integer serialization primitives

Integer serialization primitives can be classified according to the above taxonomy as well. For example:
  • There are signed and unsigned (big-endian) integer serialization primitives.
  • The size nn of the bit field to be used (1n2571\leq n\leq 257 for signed integers, 0n2560\leq n\leq 256 for unsigned integers) can either come from the top of stack or be embedded into the instruction itself.
  • If the integer xx to be serialized is not in the range 2n1x<2n1-2^{n-1}\leq x<2^{n-1} (for signed integer serialization) or 0x<2n0\leq x<2^n (for unsigned integer serialization), a range check exception is usually generated, and if nn bits cannot be stored into the provided Builder, a cell overflow exception is usually generated.
  • Quiet versions of serialization instructions do not throw exceptions; instead, they push 1-1 on top of the resulting Builder upon success, or return the original Builder with 00 on top of it to indicate failure.
Integer serialization instructions have mnemonics like STU 20\texttt{STU 20} (“store an unsigned 20-bit integer value”) or STIXQ\texttt{STIXQ} (“quietly store an integer value of variable length provided in the stack”). The full list of these instructions—including their mnemonics, descriptions, and opcodes—is provided in A.7.1.

3.2.8. Integers in cells are big-endian by default

Notice that the default order of bits in Integers serialized into Cells is big-endian, not little-endian.14 In this respect TVM is a big-endian machine. However, this affects only the serialization of integers inside cells. The internal representation of the Integer value type is implementation-dependent and irrelevant for the operation of TVM. Besides, there are some special primitives such as STULE\texttt{STULE} for (de)serializing little-endian integers, which must be stored into an integral number of bytes (otherwise “little-endianness” does not make sense, unless one is also willing to revert the order of bits inside octets). Such primitives are useful for interfacing with the little-endian world—for instance, for parsing custom-format messages arriving to a TON Blockchain smart contract from the outside world.

3.2.9. Other serialization primitives

Other cell creation primitives serialize bitstrings (i.e., cell slices without references), either taken from the stack or supplied as literal arguments; cell slices (which are concatenated to the cell builder in an obvious way); other Builders (which are also concatenated); and cell references (STREF\texttt{STREF}).

3.2.10. Other cell creation primitives

In addition to the cell serialization primitives for certain built-in value types described above, there are simple primitives that create a new empty Builder and push it into the stack (NEWC\texttt{NEWC}), or transform a Builder into a Cell (ENDC\texttt{ENDC}), thus finishing the cell creation process. An ENDC\texttt{ENDC} can be combined with a STREF\texttt{STREF} into a single instruction ENDCST\texttt{ENDCST}, which finishes the creation of a cell and immediately stores a reference to it in an “outer” Builder. There are also primitives that obtain the quantity of data bits or references already stored in a Builder, and check how many data bits or references can be stored.

3.2.11. Taxonomy of cell deserialisation primitives

Cell parsing, or deserialization, primitives can be classified as described in 3.2.6, with the following modifications:
  • They work with Slices (representing the remainder of the cell being parsed) instead of Builders.
  • They return deserialized values instead of accepting them as arguments.
  • They may come in two flavors, depending on whether they remove the deserialized portion from the Slice supplied (“fetch operations”) or leave it unmodified (“prefetch operations”).
  • Their mnemonics usually begin with LD\texttt{LD} (or PLD\texttt{PLD} for prefetch operations) instead of ST\texttt{ST}.
For example, an unsigned big-endian 20-bit integer previously serialized into a cell by a STU 20\texttt{STU 20} instruction is likely to be deserialized later by a matching LDU 20\texttt{LDU 20} instruction. Again, more detailed information about these instructions is provided in A.7.2.

3.2.12. Other cell slice primitives

In addition to the cell deserialisation primitives outlined above, TVM provides some obvious primitives for initializing and completing the cell deserialization process. For instance, one can convert a Cell into a Slice (CTOS\texttt{CTOS}), so that its deserialisation might begin; or check whether a Slice is empty, and generate an exception if it is not (ENDS\texttt{ENDS}); or deserialize a cell reference and immediately convert it into a Slice (LDREFTOS\texttt{LDREFTOS}, equivalent to two instructions LDREF\texttt{LDREF} and CTOS\texttt{CTOS}).

3.2.13. Modifying a serialized value in a cell

The reader might wonder how the values serialized inside a cell may be modified. Suppose a cell contains three serialized 29-bit integers, (x,y,z)(x,y,z), representing the coordinates of a point in space, and we want to replace yy with y=y+1y'=y+1, leaving the other coordinates intact. How would we achieve this? TVM does not offer any ways to modify existing values (cf. 2.3.4 and 2.3.5), so our example can only be accomplished with a series of operations as follows:
  1. Deserialize the original cell into three Integers xx, yy, zz in the stack (e.g., by CTOS\texttt{CTOS}; LDI 29\texttt{LDI 29}; LDI 29\texttt{LDI 29}; LDI 29\texttt{LDI 29}; ENDS\texttt{ENDS}).
  2. Increase yy by one (e.g., by SWAP\texttt{SWAP}; INC\texttt{INC}; SWAP\texttt{SWAP}).
  3. Finally, serialize the resulting Integers into a new cell (e.g., by XCHG s2\texttt{XCHG s2}; NEWC\texttt{NEWC}; STI 29\texttt{STI 29}; STI 29\texttt{STI 29}; STI 29\texttt{STI 29}; ENDC\texttt{ENDC}).

3.2.14. Modifying the persistent storage of a smart contract

If the TVM code wants to modify its persistent storage, represented by the tree of cells rooted at c4\texttt{c4}, it simply needs to rewrite control register c4\texttt{c4} by the root of the tree of cells containing the new value of its persistent storage. (If only part of the persistent storage needs to be modified, cf. 3.2.13.)

3.3 Hashmaps, or dictionaries

Hashmaps, or dictionaries, are a specific data structure represented by a tree of cells. Essentially, a hashmap represents a map from keys, which are bitstrings of either fixed or variable length, into values of an arbitrary type XX, in such a way that fast lookups and modifications be possible. While any such structure might be inspected or modified with the aid of generic cell serialization and deserialization primitives, TVM introduces special primitives to facilitate working with these hashmaps.

3.3.1. Basic hashmap types

The two most basic hashmap types predefined in TVM are HashmapE nn XX or HashmapE(n,X)(n,X), which represents a partially defined map from nn-bit strings (called keys) for some fixed 0n10230\leq n\leq 1023 into values of some type XX, and Hashmap(n,X)(n,X), which is similar to HashmapE(n,X)(n,X) but is not allowed to be empty (i.e., it must contain at least one key-value pair). Other hashmap types are also available—for example, one with keys of arbitrary length up to some predefined bound (up to 1023 bits).

3.3.2. Hashmaps as Patricia trees

The abstract representation of a hashmap in TVM is a Patricia tree, or a compact binary trie. It is a binary tree with edges labelled by bitstrings, such that the concatenation of all edge labels on a path from the root to a leaf equals a key of the hashmap. The corresponding value is kept in this leaf (for hashmaps with keys of fixed length), or optionally in the intermediate vertices as well (for hashmaps with keys of variable length). Furthermore, any intermediate vertex must have two children, and the label of the left child must begin with a binary zero, while the label of the right child must begin with a binary one. This enables us not to store the first bit of the edge labels explicitly. It is easy to see that any collection of key-value pairs (with distinct keys) is represented by a unique Patricia tree.

3.3.3. Serialization of hashmaps

The serialization of a hashmap into a tree of cells (or, more generally, into a Slice) is defined by the following TL-B scheme:15
bit#_ _:(## 1) = Bit;

hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) 
          {n = (~m) + l} node:(HashmapNode m X) = Hashmap n X;

hmn_leaf#_ {X:Type} value:X = HashmapNode 0 X;
hmn_fork#_ {n:#} {X:Type} left:^(Hashmap n X) 
           right:^(Hashmap n X) = HashmapNode (n + 1) X;

hml_short$0 {m:#} {n:#} len:(Unary ~n) 
            s:(n * Bit) = HmLabel ~n m;
hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m;
hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m;

unary_zero$0 = Unary ~0;
unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1);

hme_empty$0 {n:#} {X:Type} = HashmapE n X;
hme_root$1 {n:#} {X:Type} root:^(Hashmap n X) = HashmapE n X;

true#_ = True;
_ {n:#} _:(Hashmap n True) = BitstringSet n;

3.3.4. Brief explanation of TL-B schemes

A TL-B scheme, like the one above, includes the following components. The right-hand side of each “equation” is a type, either simple (such as Bit\texttt{Bit} or True\texttt{True}) or parametrized (such as Hashmap\texttt{Hashmap} nn XX). The parameters of a type must be either natural numbers (i.e., non-negative integers, which are required to fit into 32 bits in practice), such as nn in Hashmap\texttt{Hashmap} nn XX, or other types, such as XX in Hashmap\texttt{Hashmap} nn XX. The left-hand side of each equation describes a way to define, or even to serialize, a value of the type indicated in the right-hand side. Such a description begins with the name of a constructor, such as hm_edge\texttt{hm\_edge} or hml_long\texttt{hml\_long}, immediately followed by an optional constructor tag, such as #_ or $10, which describes the bitstring used to encode (serialize) the constructor in question. Such tags may be given in either binary (after a dollar sign) or hexadecimal notation (after a hash sign), using the conventions described in 1.0. If a tag is not explicitly provided, TL-B computes a default 32-bit constructor tag by hashing the text of the “equation” defining this constructor in a certain fashion. Therefore, empty tags must be explicitly provided by #_ or $_. All constructor names must be distinct, and constructor tags for the same type must constitute a prefix code (otherwise the deserialization would not be unique). The constructor and its optional tag are followed by field definitions. Each field definition is of the form ident:type-expr\textit{ident}:\textit{type-expr}, where ident\textit{ident} is an identifier with the name of the field16 (replaced by an underscore for anonymous fields), and type-expr\textit{type-expr} is the field’s type. The type provided here is a type expression, which may include simple types or parametrized types with suitable parameters. Variables—i.e., the (identifiers of the) previously defined fields of types #\# (natural numbers) or Type\textit{Type} (type of types)—may be used as parameters for the parametrized types. The serialization process recursively serializes each field according to its type, and the serialization of a value ultimately consists of the concatenation of bitstrings representing the constructor (i.e., the constructor tag) and the field values. Some fields may be implicit. Their definitions are surrounded by curly braces, which indicate that the field is not actually present in the serialization, but that its value must be deduced from other data (usually the parameters of the type being serialized). Some occurrences of “variables” (i.e., already-defined fields) are prefixed by a tilde. This indicates that the variable’s occurrence is used in the opposite way of the default behavior: in the left-hand side of the equation, it means that the variable will be deduced (computed) based on this occurrence, instead of substituting its previously computed value; in the right-hand side, conversely, it means that the variable will not be deduced from the type being serialized, but rather that it will be computed during the deserialization process. In other words, a tilde transforms an “input argument” into an “output argument”, and vice versa.17 Finally, some equalities may be included in curly brackets as well. These are certain “equations”, which must be satisfied by the “variables” included in them. If one of the variables is prefixed by a tilde, its value will be uniquely determined by the values of all other variables participating in the equation (which must be known at this point) when the definition is processed from the left to the right. A caret (^) preceding a type XX means that instead of serializing a value of type XX as a bitstring inside the current cell, we place this value into a separate cell, and add a reference to it into the current cell. Therefore ^XX means “the type of references to cells containing values of type XX”. Parametrized type #<= p\texttt{\#<= }p with p:#p:\texttt{\#} (this notation means ”pp of type #\texttt{\#}”, i.e., a natural number) denotes the subtype of the natural numbers type #\texttt{\#}, consisting of integers 0p0\ldots p; it is serialized into log2(p+1)\lceil\log_2(p+1)\rceil bits as an unsigned big-endian integer. Type #\texttt{\#} by itself is serialized as an unsigned 32-bit integer. Parametrized type ## b\texttt{\#\# }b with b:#<=31b:\texttt{\#<=}31 is equivalent to #<= 2b1\texttt{\#<= }2^b-1 (i.e., it is an unsigned bb-bit integer).

3.3.5. Application to the serialization of hashmaps

Let us explain the net result of applying the general rules described in 3.3.4 to the TL-B scheme presented in 3.3.3. Suppose we wish to serialize a value of type HashmapE nn XX for some integer 0n10230\leq n\leq 1023 and some type XX (i.e., a dictionary with nn-bit keys and values of type XX, admitting an abstract representation as a Patricia tree (cf. 3.3.2)). First of all, if our dictionary is empty, it is serialized into a single binary 0\texttt{0}, which is the tag of nullary constructor hme_empty\texttt{hme\_empty}. Otherwise, its serialization consists of a binary 1\texttt{1} (the tag of hme_root\texttt{hme\_root}), along with a reference to a cell containing the serialization of a value of type Hashmap nn XX (i.e., a necessarily non-empty dictionary). The only way to serialize a value of type Hashmap nn XX is given by the hm_edge\texttt{hm\_edge} constructor, which instructs us to serialize first the label label\texttt{label} of the edge leading to the root of the subtree under consideration (i.e., the common prefix of all keys in our (sub)dictionary). This label is of type HmLabel\texttt{HmLabel} ll^\perp nn, which means that it is a bitstring of length at most nn, serialized in such a way that the true length ll of the label, 0ln0\leq l\leq n, becomes known from the serialization of the label. (This special serialization method is described separately in 3.3.6.) The label must be followed by the serialization of a node\texttt{node} of type HashmapNode\texttt{HashmapNode} mm XX, where m=nlm=n-l. It corresponds to a vertex of the Patricia tree, representing a non-empty subdictionary of the original dictionary with mm-bit keys, obtained by removing from all the keys of the original subdictionary their common prefix of length ll. If m=0m=0, a value of type HashmapNode\texttt{HashmapNode} 00 XX is given by the hmn_leaf\texttt{hmn\_leaf} constructor, which describes a leaf of the Patricia tree—or, equivalently, a subdictionary with 00-bit keys. A leaf simply consists of the corresponding value\texttt{value} of type XX and is serialized accordingly. On the other hand, if m>0m>0, a value of type HashmapNode\texttt{HashmapNode} mm XX corresponds to a fork (i.e., an intermediate node) in the Patricia tree, and is given by the hmn_fork\texttt{hmn\_fork} constructor. Its serialization consists of left\texttt{left} and right\texttt{right}, two references to cells containing values of type Hashmap\texttt{Hashmap} m1m-1 XX, which correspond to the left and the right child of the intermediate node in question—or, equivalently, to the two subdictionaries of the original dictionary consisting of key-value pairs with keys beginning with a binary 0\texttt{0} or a binary 1\texttt{1}, respectively. Because the first bit of all keys in each of these subdictionaries is known and fixed, it is removed, and the resulting (necessarily non-empty) subdictionaries are recursively serialized as values of type Hashmap\texttt{Hashmap} m1m-1 XX.

3.3.6. Serialization of labels

There are several ways to serialize a label of length at most nn, if its exact length is lnl\leq n (recall that the exact length must be deducible from the serialization of the label itself, while the upper bound nn is known before the label is serialized or deserialized). These ways are described by the three constructors hml_short\texttt{hml\_short}, hml_long\texttt{hml\_long}, and hml_same\texttt{hml\_same} of type HmLabel\texttt{HmLabel} ll^\perp nn:
  • hml_short\texttt{hml\_short} — Describes a way to serialize “short” labels, of small length lnl\leq n. Such a serialization consists of a binary 0\texttt{0} (the constructor tag of hml_short\texttt{hml\_short}), followed by ll binary 1\texttt{1}s and one binary 0\texttt{0} (the unary representation of the length ll), followed by ll bits comprising the label itself.
  • hml_long\texttt{hml\_long} — Describes a way to serialize “long” labels, of arbitrary length lnl\leq n. Such a serialization consists of a binary 10\texttt{10} (the constructor tag of hml_long\texttt{hml\_long}), followed by the big-endian binary representation of the length 0ln0\leq l\leq n in log2(n+1)\lceil\log_2(n+1)\rceil bits, followed by ll bits comprising the label itself.
  • hml_same\texttt{hml\_same} — Describes a way to serialize “long” labels, consisting of ll repetitions of the same bit vv. Such a serialization consists of 11\texttt{11} (the constructor tag of hml_same\texttt{hml\_same}), followed by the bit vv, followed by the length ll stored in log2(n+1)\lceil\log_2(n+1)\rceil bits as before.
Each label can always be serialized in at least two different fashions, using hml_short\texttt{hml\_short} or hml_long\texttt{hml\_long} constructors. Usually the shortest serialization (and in the case of a tie—the lexicographically smallest among the shortest) is preferred and is generated by TVM hashmap primitives, while the other variants are still considered valid. This label encoding scheme has been designed to be efficient for dictionaries with “random” keys (e.g., hashes of some data), as well as for dictionaries with “regular” keys (e.g., big-endian representations of integers in some range).

3.3.7. An example of dictionary serialization

Consider a dictionary with three 16-bit keys 13, 17, and 239 (considered as big-endian integers) and corresponding 16-bit values 169, 289, and 57121. In binary form:
0000000000001101 => 0000000010101001
0000000000010001 => 0000000100100001
0000000011101111 => 1101111100100001
The corresponding Patricia tree consists of a root AA, two intermediate nodes BB and CC, and three leaf nodes DD, EE, and FF, corresponding to 13, 17, and 239, respectively. The root AA has only one child, BB; the label on the edge ABAB is 00000000=0800000000=0^8. The node BB has two children: its left child is an intermediate node CC with the edge BCBC labelled by (0)00(0)00, while its right child is the leaf FF with BFBF labelled by (1)1101111(1)1101111. Finally, CC has two leaf children DD and EE, with CDCD labelled by (0)1101(0)1101 and CECE—by (1)0001(1)0001. The corresponding value of type HashmapE\texttt{HashmapE} 16 (##\texttt{\#\#} 16) may be written in human-readable form as:
(hme_root$1 
  root:^(hm_edge label:(hml_same$11 v:0 n:8) node:(hm_fork 
    left:^(hm_edge label:(hml_short$0 len:$110 s:$00) 
      node:(hm_fork
        left:^(hm_edge label:(hml_long$10 n:4 s:$1101) 
          node:(hm_leaf value:169))
        right:^(hm_edge label:(hml_long$10 n:4 s:$0001) 
          node:(hm_leaf value:289))))
    right:^(hm_edge label:(hml_long$10 n:7 s:$1101111) 
      node:(hm_leaf value:57121)))))
The serialization of this data structure into a tree of cells consists of six cells with the following binary data contained in them:
A := 1
A.0 := 11 0 01000 
A.0.0 := 0 110 00
A.0.0.0 := 10 100 1101 0000000010101001
A.0.0.1 := 10 100 0001 0000000100100001
A.0.1 := 10 111 1101111 1101111100100001
Here AA is the root cell, A.0A.0 is the cell at the first reference of AA, A.1A.1 is the cell at the second reference of AA, and so on. This tree of cells can be represented more compactly using the hexadecimal notation described in 1.0, using indentation to reflect the tree-of-cells structure:
C_
 C8
  62_
   A68054C_
   A08090C_
  BEFDF21
A total of 93 data bits and 5 references in 6 cells have been used to serialize this dictionary. Notice that a straightforward representation of three 16-bit keys and their corresponding 16-bit values would already require 96 bits (albeit without any references), so this particular serialization turns out to be quite efficient.

3.3.8. Ways to describe the serialization of type XX

Notice that the built-in TVM primitives for dictionary manipulation need to know something about the serialization of type XX; otherwise, they would not be able to work correctly with Hashmap\mathit{Hashmap} nn XX, because values of type XX are immediately contained in the Patricia tree leaf cells. There are several options available to describe the serialization of type XX:
  • The simplest case is when X=^YX = \text{\textasciicircum}Y for some other type YY. In this case the serialization of XX itself always consists of one reference to a cell, which in fact must contain a value of type YY, something that is not relevant for dictionary manipulation primitives.
  • Another simple case is when the serialization of any value of type XX always consists of 0b10230\leq b\leq 1023 data bits and 0r40\leq r\leq 4 references. Integers bb and rr can then be passed to a dictionary manipulation primitive as a simple description of XX. (Notice that the previous case corresponds to b=0b=0, r=1r=1.)
  • A more sophisticated case can be described by four integers 1b0,b110231\leq b_0,b_1\leq 1023, 0r0,r140\leq r_0,r_1\leq 4, with bib_i and rir_i used when the first bit of the serialization equals ii. When b0=b1b_0=b_1 and r0=r1r_0=r_1, this case reduces to the previous one.
  • Finally, the most general description of the serialization of a type XX is given by a splitting function splitX\mathit{split}_X for XX, which accepts one Slice parameter ss, and returns two Slices, ss' and ss'', where ss' is the only prefix of ss that is the serialization of a value of type XX, and ss'' is the remainder of ss. If no such prefix exists, the splitting function is expected to throw an exception. Notice that a compiler for a high-level language, which supports some or all algebraic TL-B types, is likely to automatically generate splitting functions for all types defined in the program.

3.3.9. A simplifying assumption on the serialization of XX

One may notice that values of type XX always occupy the remaining part of an hm_edge\texttt{hm\_edge}/hme_leaf\texttt{hme\_leaf} cell inside the serialization of a HashmapE nn XX. Therefore, if we do not insist on strict validation of all dictionaries accessed, we may assume that everything left unparsed in an hm_edge\texttt{hm\_edge}/hme_leaf\texttt{hme\_leaf} cell after deserializing its label\texttt{label} is a value of type XX. This greatly simplifies the creation of dictionary manipulation primitives, because in most cases they turn out not to need any information about XX at all.

3.3.10. Basic dictionary operations

Let us present a classification of basic operations with dictionaries (i.e., values DD of type HashmapE nn XX):
  • Get(D,k)\text{Get}(D,k) — Given DD:HashmapE(n,X)(n,X) and a key k:nbitk:n\cdot\texttt{bit}, returns the corresponding value D[k]:X?D[k]:X^? kept in DD.
  • Set(D,k,x)\text{Set}(D,k,x) — Given DD:HashmapE(n,X)(n,X), a key k:nbitk:n\cdot\texttt{bit}, and a value x:Xx:X, sets D[k]D'[k] to xx in a copy DD' of DD, and returns the resulting dictionary DD' (cf. 2.3.4).
  • Add(D,k,x)\text{Add}(D,k,x) — Similar to Set\text{Set}, but adds the key-value pair (k,x)(k,x) to DD only if key kk is absent in DD.
  • Replace(D,k,x)\text{Replace}(D,k,x) — Similar to Set\text{Set}, but changes D[k]D'[k] to xx only if key kk is already present in DD.
  • GetSet\text{GetSet}, GetAdd\text{GetAdd}, GetReplace\text{GetReplace} — Similar to Set\text{Set}, Add\text{Add}, and Replace\text{Replace}, respectively, but returns the old value of D[k]D[k] as well.
  • Delete(D,k)\text{Delete}(D,k) — Deletes key kk from dictionary DD, and returns the resulting dictionary DD'.
  • GetMin(D)\text{GetMin}(D), GetMax(D)\text{GetMax}(D) — Gets the minimal or maximal key kk from dictionary DD, along with the associated value x:Xx:X.
  • RemoveMin(D)\text{RemoveMin}(D), RemoveMax(D)\text{RemoveMax}(D) — Similar to GetMin\text{GetMin} and GetMax\text{GetMax}, but also removes the key in question from dictionary DD, and returns the modified dictionary DD'. May be used to iterate over all elements of DD, effectively using (a copy of) DD itself as an iterator.
  • GetNext(D,k)\text{GetNext}(D,k) — Computes the minimal key k>kk'>k (or kkk'\geq k in a variant) and returns it along with the corresponding value x:Xx':X. May be used to iterate over all elements of DD.
  • GetPrev(D,k)\text{GetPrev}(D,k) — Computes the maximal key k<kk'<k (or kkk'\leq k in a variant) and returns it along with the corresponding value x:Xx':X.
  • Empty(n)\text{Empty}(n) — Creates an empty dictionary DD:HashmapE(n,X)(n,X).
  • IsEmpty(D)\text{IsEmpty}(D) — Checks whether a dictionary is empty.
  • Create(n,{(ki,xi)})\text{Create}(n,\{(k_i,x_i)\}) — Given nn, creates a dictionary from a list (ki,xi)(k_i,x_i) of key-value pairs passed in stack.
  • GetSubdict(D,l,k0)\text{GetSubdict}(D,l,k_0) — Given DD:HashmapE(n,X)(n,X) and some ll-bit string k0:lbitk_0:l\cdot\texttt{bit} for 0ln0\leq l\leq n, returns subdictionary D=D/k0D'=D/k_0 of DD, consisting of keys beginning with k0k_0. The result DD' may be of either type HashmapE(n,X)(n,X) or type HashmapE(nl,X)(n-l,X).
  • ReplaceSubdict(D,l,k0,D)\text{ReplaceSubdict}(D,l,k_0,D') — Given DD:HashmapE(n,X)(n,X), 0ln0\leq l\leq n, k0:lbitk_0:l\cdot\texttt{bit}, and DD':HashmapE(nl,X)(n-l,X), replaces with DD' the subdictionary D/k0D/k_0 of DD consisting of keys beginning with k0k_0, and returns the resulting dictionary DD'':HashmapE(n,X)(n,X). Some variants of ReplaceSubdict\text{ReplaceSubdict} may also return the old value of the subdictionary D/k0D/k_0 in question.
  • DeleteSubdict(D,l,k0)\text{DeleteSubdict}(D,l,k_0) — Equivalent to ReplaceSubdict\text{ReplaceSubdict} with DD' being an empty dictionary.
  • Split(D)\text{Split}(D) — Given DD:HashmapE(n,X)(n,X), returns D0:=D/0D_0:=D/0 and D1:=D/1D_1:=D/1:HashmapE(n1,X)(n-1,X), the two subdictionaries of DD consisting of all keys beginning with 00 and 11, respectively.
  • Merge(D0,D1)\text{Merge}(D_0,D_1) — Given D0D_0 and D1D_1:HashmapE(n1,X)(n-1,X), computes DD:HashmapE(n,X)(n,X), such that D/0=D0D/0=D_0 and D/1=D1D/1=D_1.
  • ForEach(D,f)\text{ForEach}(D,f) — Executes a function ff with two arguments kk and xx, with (k,x)(k,x) running over all key-value pairs of a dictionary DD in lexicographical order.18
  • ForEachRev(D,f)\text{ForEachRev}(D,f) — Similar to ForEach\text{ForEach}, but processes all key-value pairs in reverse order.
  • TreeReduce(D,o,f,g)\text{TreeReduce}(D,o,f,g) — Given DD:HashmapE(n,X)(n,X), a value o:Xo:X, and two functions f:XYf:X\to Y and g:Y×YYg:Y\times Y\to Y, performs a “tree reduction” of DD by first applying ff to all the leaves, and then using gg to compute the value corresponding to a fork starting from the values assigned to its children.19

3.3.11. Taxonomy of dictionary primitives

The dictionary primitives, described in detail in Appendix A.10, can be classified according to the following categories:
  • Which dictionary operation (cf. 3.3.10) do they perform?
  • Are they specialized for the case X=^YX = \text{\textasciicircum}Y If so, do they represent values of type YY by Cells or by Slices? (Generic versions always represent values of type XX as Slices.)
  • Are the dictionaries themselves passed and returned as Cells or as Slices? (Most primitives represent dictionaries as Slices.)
  • Is the key length nn fixed inside the primitive, or is it passed in the stack?
  • Are the keys represented by Slices, or by signed or unsigned Integers?
In addition, TVM includes special serialization/deserialization primitives, such as STDICT\texttt{STDICT}, LDDICT\texttt{LDDICT}, and PLDDICT\texttt{PLDDICT}. They can be used to extract a dictionary from a serialization of an encompassing object, or to insert a dictionary into such a serialization.

3.4 Hashmaps with variable-length keys

TVM provides some support for dictionaries, or hashmaps, with variable-length keys, in addition to its support for dictionaries with fixed-length keys (as described in 3.3 above).

3.4.1. Serialization of dictionaries with variable-length keys

The serialization of a VarHashmap into a tree of cells (or, more generally, into a Slice) is defined by a TL-B scheme, similar to that described in 3.3.3:
vhm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) 
           {n = (~m) + l} node:(VarHashmapNode m X) 
           = VarHashmap n X;
vhmn_leaf$00 {n:#} {X:Type} value:X = VarHashmapNode n X;
vhmn_fork$01 {n:#} {X:Type} left:^(VarHashmap n X) 
             right:^(VarHashmap n X) value:(Maybe X) 
             = VarHashmapNode (n + 1) X;
vhmn_cont$1 {n:#} {X:Type} branch:bit child:^(VarHashmap n X) 
            value:X = VarHashmapNode (n + 1) X;

nothing$0 {X:Type} = Maybe X;
just$1 {X:Type} value:X = Maybe X;

vhme_empty$0 {n:#} {X:Type} = VarHashmapE n X;
vhme_root$1 {n:#} {X:Type} root:^(VarHashmap n X) 
            = VarHashmapE n X;

3.4.2. Serialization of prefix codes

One special case of a dictionary with variable-length keys is that of a prefix code, where the keys cannot be prefixes of each other. Values in such dictionaries may occur only in the leaves of a Patricia tree. The serialization of a prefix code is defined by the following TL-B scheme:
phm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) 
           {n = (~m) + l} node:(PfxHashmapNode m X) 
           = PfxHashmap n X;

phmn_leaf$0 {n:#} {X:Type} value:X = PfxHashmapNode n X;
phmn_fork$1 {n:#} {X:Type} left:^(PfxHashmap n X) 
            right:^(PfxHashmap n X) = PfxHashmapNode (n + 1) X;

phme_empty$0 {n:#} {X:Type} = PfxHashmapE n X;
phme_root$1 {n:#} {X:Type} root:^(PfxHashmap n X) 
            = PfxHashmapE n X;

4 Control flow, continuations, and exceptions

This chapter describes continuations, which may represent execution tokens and exception handlers in TVM. Continuations are deeply involved with the control flow of a TVM program; in particular, subroutine calls and conditional and iterated execution are implemented in TVM using special primitives that accept one or more continuations as their arguments. We conclude this chapter with a discussion of the problem of recursion and of families of mutually recursive functions, exacerbated by the fact that cyclic references are not allowed in TVM data structures (including TVM code).

4.1 Continuations and subroutines

Recall (cf. 1.1.3) that Continuation values represent “execution tokens” that can be executed later—for example, by EXECUTE\texttt{EXECUTE}=CALLX\texttt{CALLX} (“execute” or “call indirect”) or JMPX\texttt{JMPX} (“jump indirect”) primitives. As such, the continuations are heavily used by control flow primitives, enabling subroutine calls, conditional expressions, loops, and so on.

4.1.1. Ordinary continuations

The most common kind of continuations are the ordinary continuations, containing the following data:
  • A Slice code\texttt{code} (cf. 1.1.3 and 3.2.2), containing (the remainder of) the TVM code to be executed.
  • A (possibly empty) Stack stack\texttt{stack}, containing the original contents of the stack for the code to be executed.
  • A (possibly empty) list save\texttt{save} of pairs (c(i),vi)(\texttt{c}(i),v_i) (also called “savelist”), containing the values of control registers to be restored before the execution of the code.
  • A 16-bit integer value cp\texttt{cp}, selecting the TVM codepage used to interpret the TVM code from code\texttt{code}.
  • An optional non-negative integer nargs\texttt{nargs}, indicating the number of arguments expected by the continuation.

4.1.2. Simple ordinary continuations

In most cases, the ordinary continuations are the simplest ones, having empty stack\texttt{stack} and save\texttt{save}. They consist essentially of a reference code\texttt{code} to (the remainder of) the code to be executed, and of the codepage cp\texttt{cp} to be used while decoding the instructions from this code.

4.1.3. Current continuation cc

The “current continuation” cc\texttt{cc} is an important part of the total state of TVM, representing the code being executed right now (cf. 1.1). In particular, what we call “the current stack” (or simply “the stack”) when discussing all other primitives is in fact the stack of the current continuation. All other components of the total state of TVM may be also thought of as parts of the current continuation cc\texttt{cc}; however, they may be extracted from the current continuation and kept separately as part of the total state for performance reasons. This is why we describe the stack, the control registers, and the codepage as separate parts of the TVM state in 1.4.

4.1.4. Normal work of TVM, or the main loop

TVM usually performs the following operations: If the current continuation cc\texttt{cc} is an ordinary one, it decodes the first instruction from the Slice code\texttt{code}, similarly to the way other cells are deserialized by TVM LD*\texttt{LD*} primitives (cf. 3.2 and 3.2.11): it decodes the opcode first, and then the parameters of the instruction (e.g., 4-bit fields indicating “stack registers” involved for stack manipulation primitives, or constant values for “push constant” or “literal” primitives). The remainder of the Slice is then put into the code\texttt{code} of the new cc\texttt{cc}, and the decoded operation is executed on the current stack. This entire process is repeated until there are no operations left in cc.code\texttt{cc.code}. If the code\texttt{code} is empty (i.e., contains no bits of data and no references), or if a (rarely needed) explicit subroutine return (RET\texttt{RET}) instruction is encountered, the current continuation is discarded, and the “return continuation” from control register c0\texttt{c0} is loaded into cc\texttt{cc} instead (this process is discussed in more detail starting in 4.1.6).20 Then the execution continues by parsing operations from the new current continuation.

4.1.5. Extraordinary continuations

In addition to the ordinary continuations considered so far (cf. 4.1.1), TVM includes some extraordinary continuations, representing certain less common states. Examples of extraordinary continuations include:
  • The continuation ec_quit\texttt{ec\_quit} with its parameter set to zero, which represents the end of the work of TVM. This continuation is the original value of c0\texttt{c0} when TVM begins executing the code of a smart contract.
  • The continuation ec_until\texttt{ec\_until}, which contains references to two other continuations (ordinary or not) representing the body of the loop being executed and the code to be executed after the loop.
Execution of an extraordinary continuation by TVM depends on its specific class, and differs from the operations for ordinary continuations described in 4.1.4.21

4.1.6. Switching to another continuation: JMP\texttt{JMP} and RET\texttt{RET}

The process of switching to another continuation cc may be performed by such instructions as JMPX\texttt{JMPX} (which takes cc from the stack) or RET\texttt{RET} (which uses c0\texttt{c0} as cc). This process is slightly more complex than simply setting the value of cc\texttt{cc} to cc: before doing this, either all values or the top nn values in the current stack are moved to the stack of the continuation cc, and only then is the remainder of the current stack discarded. If all values need to be moved (the most common case), and if the continuation cc has an empty stack (also the most common case; notice that extraordinary continuations are assumed to have an empty stack), then the new stack of cc equals the stack of the current continuation, so we can simply transfer the current stack in its entirety to cc. (If we keep the current stack as a separate part of the total state of TVM, we have to do nothing at all.)

4.1.7. Determining the number nn of arguments passed to the next continuation cc

By default, nn equals the depth of the current stack. However, if cc has an explicit value of nargs\texttt{nargs} (number of arguments to be provided), then nn is computed as nn', equal to cc.nargs\texttt{nargs} minus the current depth of cc‘s stack. Furthermore, there are special forms of JMPX\texttt{JMPX} and RET\texttt{RET} that provide an explicit value nn'', the number of parameters from the current stack to be passed to continuation cc. If nn'' is provided, it must be less than or equal to the depth of the current stack, or else a stack underflow exception occurs. If both nn' and nn'' are provided, we must have nnn'\leq n'', in which case n=nn=n' is used. If nn'' is provided and nn' is not, then n=nn=n'' is used. One could also imagine that the default value of nn'' equals the depth of the original stack, and that nn'' values are always removed from the top of the original stack even if only nn' of them are actually moved to the stack of the next continuation cc. Even though the remainder of the current stack is discarded afterwards, this description will become useful later.

4.1.8. Restoring control registers from the new continuation cc

After the new stack is computed, the values of control registers present in cc.save\texttt{save} are restored accordingly, and the current codepage cp\texttt{cp} is also set to cc.cp\texttt{cp}. Only then does TVM set cc\texttt{cc} equal to the new cc and begin its execution.22

4.1.9. Subroutine calls: CALLX\texttt{CALLX} or EXECUTE\texttt{EXECUTE} primitives

The execution of continuations as subroutines is slightly more complicated than switching to continuations. Consider the CALLX\texttt{CALLX} or EXECUTE\texttt{EXECUTE} primitive, which takes a continuation cc from the (current) stack and executes it as a subroutine. Apart from doing the stack manipulations described in 4.1.6 and 4.1.7 and setting the new control registers and codepage as described in 4.1.8, these primitives perform several additional steps:
  1. After the top nn'' values are removed from the current stack (cf. 4.1.7), the (usually empty) remainder is not discarded, but instead is stored in the (old) current continuation cc\texttt{cc}.
  2. The old value of the special register c0\texttt{c0} is saved into the (previously empty) savelist cc.save\texttt{cc.save}.
  3. The continuation cc\texttt{cc} thus modified is not discarded, but instead is set as the new c0\texttt{c0}, which performs the role of “next continuation” or “return continuation” for the subroutine being called.
  4. After that, the switching to cc continues as before. In particular, some control registers are restored from cc.save\texttt{save}, potentially overwriting the value of c0\texttt{c0} set in the previous step. (Therefore, a good optimization would be to check that c0\texttt{c0} is present in cc.save\texttt{save} from the very beginning, and skip the three previous steps as useless in this case.)
In this way, the called subroutine can return control to the caller by switching the current continuation to the return continuation saved in c0\texttt{c0}. Nested subroutine calls work correctly because the previous value of c0\texttt{c0} ends up saved into the new c0\texttt{c0}‘s control register savelist c0.save\texttt{c0.save}, from which it is restored later.

4.1.10. Determining the number of arguments passed to and/or return values accepted from a subroutine

Similarly to JMPX\texttt{JMPX} and RET\texttt{RET}, CALLX\texttt{CALLX} also has special (rarely used) forms, which allow us to explicitly specify the number nn'' of arguments passed from the current stack to the called subroutine (by default, nn'' equals the depth of the current stack, i.e., it is passed in its entirety). Furthermore, a second number nn''' can be specified, used to set nargs\texttt{nargs} of the modified cc\texttt{cc} continuation before storing it into the new c0\texttt{c0}; the new nargs\texttt{nargs} equals the depth of the old stack minus nn'' plus nn'''. This means that the caller is willing to pass exactly nn'' arguments to the called subroutine, and is willing to accept exactly nn''' results in their stead. Such forms of CALLX\texttt{CALLX} and RET\texttt{RET} are mostly intended for library functions that accept functional arguments and want to invoke them safely. Another application is related to the “virtualization support” of TVM, which enables TVM code to run other TVM code inside a “virtual TVM machine”. Such virtualization techniques might be useful for implementing sophisticated payment channels in the TON Blockchain (cf. [1, 5]).

4.1.11. CALLCC\texttt{CALLCC}: call with current continuation

Notice that TVM supports a form of the “call with current continuation” primitive. Namely, primitive CALLCC\texttt{CALLCC} is similar to CALLX\texttt{CALLX} or JMPX\texttt{JMPX} in that it takes a continuation cc from the stack and switches to it; however, CALLCC\texttt{CALLCC} does not discard the previous current continuation cc' (as JMPX\texttt{JMPX} does) and does not write cc' to c0\texttt{c0} (as CALLX\texttt{CALLX} does), but rather pushes cc' into the (new) stack as an extra argument to cc. The primitive JMPXDATA\texttt{JMPXDATA} does a similar thing, but pushes only the (remainder of the) code of the previous current continuation as a Slice.

4.2 Control flow primitives: conditional and iterated execution

4.2.1. Conditional execution: IF\texttt{IF}, IFNOT\texttt{IFNOT}, IFELSE\texttt{IFELSE}

An important modification of EXECUTE\texttt{EXECUTE} (or CALLX\texttt{CALLX}) consists in its conditional forms. For example, IF\texttt{IF} accepts an integer xx and a continuation cc, and executes cc (in the same way as EXECUTE\texttt{EXECUTE} would do it) only if xx is non-zero; otherwise both values are simply discarded from the stack. Similarly, IFNOT\texttt{IFNOT} accepts xx and cc, but executes cc only if x=0x=0. Finally, IFELSE\texttt{IFELSE} accepts xx, cc, and cc', removes these values from the stack, and executes cc if x0x\neq0 or cc' if x=0x=0.

4.2.2. Iterated execution and loops

More sophisticated modifications of EXECUTE\texttt{EXECUTE} include:
  • REPEAT\texttt{REPEAT} — Takes an integer nn and a continuation cc, and executes cc nn times.23
  • WHILE\texttt{WHILE} — Takes cc' and cc'', executes cc', and then takes the top value xx from the stack. If xx is non-zero, it executes cc'' and then begins a new loop by executing cc' again; if xx is zero, it stops.
  • UNTIL\texttt{UNTIL} — Takes cc, executes it, and then takes the top integer xx from the stack. If xx is zero, a new iteration begins; if xx is non-zero, the previously executed code is resumed.

4.2.3. Constant, or literal, continuations

We see that we can create arbitrarily complex conditional expressions and loops in the TVM code, provided we have a means to push constant continuations into the stack. In fact, TVM includes special versions of “literal” or “constant” primitives that cut the next nn bytes or bits from the remainder of the current code cc.code\texttt{cc.code} into a cell slice, and then push it into the stack not as a Slice (as a PUSHSLICE\texttt{PUSHSLICE} does) but as a simple ordinary Continuation (which has only code\texttt{code} and cp\texttt{cp}). The simplest of these primitives is PUSHCONT\texttt{PUSHCONT}, which has an immediate argument nn describing the number of subsequent bytes (in a byte-oriented version of TVM) or bits to be converted into a simple continuation. Another primitive is PUSHREFCONT\texttt{PUSHREFCONT}, which removes the first cell reference from the current continuation cc.code\texttt{cc.code}, converts the cell referred to into a cell slice, and finally converts the cell slice into a simple continuation.

4.2.4. Constant continuations combined with conditional or iterated execution primitives

Because constant continuations are very often used as arguments to conditional or iterated execution primitives, combined versions of these primitives (e.g., IFCONT\texttt{IFCONT} or UNTILREFCONT\texttt{UNTILREFCONT}) may be defined in a future revision of TVM, which combine a PUSHCONT\texttt{PUSHCONT} or PUSHREFCONT\texttt{PUSHREFCONT} with another primitive. If one inspects the resulting code, IFCONT\texttt{IFCONT} looks very much like the more customary “conditional-branch-forward” instruction.

4.3 Operations with continuations

4.3.1 Continuations are opaque

Notice that all continuations are opaque, at least in the current version of TVM, meaning that there is no way to modify a continuation or inspect its internal data. Almost the only use of a continuation is to supply it to a control flow primitive. While there are some arguments in favor of including support for non-opaque continuations in TVM (along with opaque continuations, which are required for virtualization), the current revision offers no such support.

4.3.2. Allowed operations with continuations

However, some operations with opaque continuations are still possible, mostly because they are equivalent to operations of the kind “create a new continuation, which will do something special, and then invoke the original continuation”. Allowed operations with continuations include:
  • Push one or several values into the stack of a continuation cc (thus creating a partial application of a function, or a closure).
  • Set the saved value of a control register c(i)\texttt{c}(i) inside the savelist cc.save\texttt{save} of a continuation cc. If there is already a value for the control register in question, this operation silently does nothing.

4.3.3. Example: operations with control registers

TVM has some primitives to set and inspect the values of control registers. The most important of them are PUSH c(i)\texttt{PUSH c}(i) (pushes the current value of c(i)\texttt{c}(i) into the stack) and POP c(i)\texttt{POP c}(i) (sets the value of c(i)\texttt{c}(i) from the stack, if the supplied value is of the correct type). However, there is also a modified version of the latter instruction, called POPSAVE c(i)\texttt{POPSAVE c}(i), which saves the old value of c(i)\texttt{c}(i) (for i>0i>0) into the continuation at c0\texttt{c0} as described in 4.3.2 before setting the new value.

4.3.4. Example: setting the number of arguments to a function in its code

The primitive LEAVEARGS\texttt{LEAVEARGS} nn demonstrates another application of continuations in an operation: it leaves only the top nn values of the current stack, and moves the remainder to the stack of the continuation in c0\texttt{c0}. This primitive enables a called function to “return” unneeded arguments to its caller’s stack, which is useful in some situations (e.g., those related to exception handling).

4.3.5. Boolean circuits

A continuation cc may be thought of as a piece of code with two optional exit points kept in the savelist of cc: the principal exit point given by cc.c0\texttt{c0}:=cc.save\texttt{save}(c0\texttt{c0}), and the auxiliary exit point given by cc.c1\texttt{c1}:=cc.save\texttt{save}(c1\texttt{c1}). If executed, a continuation performs whatever action it was created for, and then (usually) transfers control to the principal exit point, or, on some occasions, to the auxiliary exit point. We sometimes say that a continuation cc with both exit points cc.c0\texttt{c0} and cc.c1\texttt{c1} defined is a two-exit continuation, or a boolean circuit, especially if the choice of the exit point depends on some internally-checked condition.

4.3.6. Composition of continuations

One can compose two continuations cc and cc' simply by setting cc.c0\texttt{c0} or cc.c1\texttt{c1} to cc'. This creates a new continuation denoted by c0cc\circ_0c' or c1cc\circ_1c', which differs from cc in its savelist. (Recall that if the savelist of cc already has an entry corresponding to the control register in question, such an operation silently does nothing as explained in 4.3.2). By composing continuations, one can build chains or other graphs, possibly with loops, representing the control flow. In fact, the resulting graph resembles a flow chart, with the boolean circuits corresponding to the “condition nodes” (containing code that will transfer control either to c0\texttt{c0} or to c1\texttt{c1} depending on some condition), and the one-exit continuations corresponding to the “action nodes”.

4.3.7. Basic continuation composition primitives

Two basic primitives for composing continuations are COMPOS\texttt{COMPOS} (also known as SETCONT c0\texttt{SETCONT c0} and BOOLAND\texttt{BOOLAND}) and COMPOSALT\texttt{COMPOSALT} (also known as SETCONT c1\texttt{SETCONT c1} and BOOLOR\texttt{BOOLOR}), which take cc and cc' from the stack, set cc.c0\texttt{c0} or cc.c1\texttt{c1} to cc', and return the resulting continuation c=c0cc''=c\circ_0c' or c1cc\circ_1c'. All other continuation composition operations can be expressed in terms of these two primitives.

4.3.8. Advanced continuation composition primitives

However, TVM can compose continuations not only taken from stack, but also taken from c0\texttt{c0} or c1\texttt{c1}, or from the current continuation cc\texttt{cc}; likewise, the result may be pushed into the stack, stored into either c0\texttt{c0} or c1\texttt{c1}, or used as the new current continuation (i.e., the control may be transferred to it). Furthermore, TVM can define conditional composition primitives, performing some of the above actions only if an integer value taken from the stack is non-zero. For instance, EXECUTE\texttt{EXECUTE} can be described as ccc0cc\texttt{cc}\leftarrow c\circ_0\texttt{cc}, with continuation cc taken from the original stack. Similarly, JMPX\texttt{JMPX} is ccc\texttt{cc}\leftarrow c, and RET\texttt{RET} (also known as RETTRUE\texttt{RETTRUE} in a boolean circuit context) is ccc0\texttt{cc}\leftarrow\texttt{c0}. Other interesting primitives include THENRET\texttt{THENRET} (cc0c0c'\leftarrow c\circ_0\texttt{c0}) and ATEXIT\texttt{ATEXIT} (c0c0c0\texttt{c0}\leftarrow c\circ_0\texttt{c0}). Finally, some “experimental” primitives also involve c1\texttt{c1} and 1\circ_1. For example:
  • RETALT\texttt{RETALT} or RETFALSE\texttt{RETFALSE} does ccc1\texttt{cc}\leftarrow\texttt{c1}.
  • Conditional versions of RET\texttt{RET} and RETALT\texttt{RETALT} may also be useful: RETBOOL\texttt{RETBOOL} takes an integer xx from the stack, and performs RETTRUE\texttt{RETTRUE} if x0x\neq0, RETFALSE\texttt{RETFALSE} otherwise.
  • INVERT\texttt{INVERT} does c0c1\texttt{c0}\leftrightarrow\texttt{c1}; if the two continuations in c0\texttt{c0} and c1\texttt{c1} represent the two branches we should select depending on some boolean expression, INVERT\texttt{INVERT} negates this expression on the outer level.
  • INVERTCONT\texttt{INVERTCONT} does cc.c0c\texttt{c0}\leftrightarrow c.c1\texttt{c1} to a continuation cc taken from the stack.
  • Variants of ATEXIT\texttt{ATEXIT} include ATEXITALT\texttt{ATEXITALT} (c1c1c1\texttt{c1}\leftarrow c\circ_1\texttt{c1}) and SETEXITALT\texttt{SETEXITALT} (c1(c0c0)1c1\texttt{c1}\leftarrow (c\circ_0\texttt{c0})\circ_1\texttt{c1}).
  • BOOLEVAL\texttt{BOOLEVAL} takes a continuation cc from the stack and does cc((c0(PUSH1))1(PUSH0))0cc\texttt{cc}\leftarrow \bigl((c\circ_0(\texttt{PUSH}\,-1))\circ_1(\texttt{PUSH}\,0)\bigr)\circ_0\texttt{cc}. If cc represents a boolean circuit, the net effect is to evaluate it and push either 1-1 or 00 into the stack before continuing.

4.4 Continuations as objects

4.4.1. Representing objects using continuations

Object-oriented programming in Smalltalk (or Objective C) style may be implemented with the aid of continuations. For this, an object is represented by a special continuation oo. If it has any data fields, they can be kept in the stack of oo, making oo a partial application (i.e., a continuation with a non-empty stack). When somebody wants to invoke a method mm of oo with arguments x1x_1, x2x_2, \ldots, xnx_n, she pushes the arguments into the stack, then pushes a magic number corresponding to the method mm, and then executes oo passing n+1n+1 arguments (cf. 4.1.10). Then oo uses the top-of-stack integer mm to select the branch with the required method, and executes it. If oo needs to modify its state, it simply computes a new continuation oo' of the same sort (perhaps with the same code as oo, but with a different initial stack). The new continuation oo' is returned to the caller along with whatever other return values need to be returned.

4.4.2. Serializable objects

Another way of representing Smalltalk-style objects as continuations, or even as trees of cells, consists in using the JMPREFDATA\texttt{JMPREFDATA} primitive (a variant of JMPXDATA\texttt{JMPXDATA}, cf. 4.1.11), which takes the first cell reference from the code of the current continuation, transforms the cell referred to into a simple ordinary continuation, and transfers control to it, first pushing the remainder of the current continuation as a Slice into the stack. In this way, an object might be represented by a cell o~\tilde o that contains JMPREFDATA\texttt{JMPREFDATA} at the beginning of its data, and the actual code of the object in the first reference (one might say that the first reference of cell o~\tilde o is the class of object o~\tilde o). Remaining data and references of this cell will be used for storing the fields of the object. Such objects have the advantage of being trees of cells, and not just continuations, meaning that they can be stored into the persistent storage of a TON smart contract.

4.4.3. Unique continuations and capabilities

It might make sense (in a future revision of TVM) to mark some continuations as unique, meaning that they cannot be copied, even in a delayed manner, by increasing their reference counter to a value greater than one. If an opaque continuation is unique, it essentially becomes a capability, which can either be used by its owner exactly once or be transferred to somebody else. For example, imagine a continuation that represents the output stream to a printer (this is an example of a continuation used as an object, cf. 4.4.1). When invoked with one integer argument nn, this continuation outputs the character with code nn to the printer, and returns a new continuation of the same kind reflecting the new state of the stream. Obviously, copying such a continuation and using the two copies in parallel would lead to some unintended side effects; marking it as unique would prohibit such adverse usage.

4.5 Exception handling

TVM’s exception handling is quite simple and consists in a transfer of control to the continuation kept in control register c2\texttt{c2}.

4.5.1. Two arguments of the exception handler: exception parameter and exception number

Every exception is characterized by two arguments: the exception number (an Integer) and the exception parameter (any value, most often a zero Integer). Exception numbers 0–31 are reserved for TVM, while all other exception numbers are available for user-defined exceptions.

4.5.2. Primitives for throwing an exception

There are several special primitives used for throwing an exception. The most general of them, THROWANY\texttt{THROWANY}, takes two arguments, vv and 0n<2160\leq n<2^{16}, from the stack, and throws the exception with number nn and value vv. There are variants of this primitive that assume vv to be a zero integer, store nn as a literal value, and/or are conditional on an integer value taken from the stack. User-defined exceptions may use arbitrary values as vv (e.g., trees of cells) if needed.

4.5.3. Exceptions generated by TVM

Of course, some exceptions are generated by normal primitives. For example, an arithmetic overflow exception is generated whenever the result of an arithmetic operation does not fit into a signed 257-bit integer. In such cases, the arguments of the exception, vv and nn, are determined by TVM itself.

4.5.4. Exception handling

The exception handling itself consists in a control transfer to the exception handler—i.e., the continuation specified in control register c2\texttt{c2}, with vv and nn supplied as the two arguments to this continuation, as if a JMP\texttt{JMP} to c2\texttt{c2} had been requested with n=2n''=2 arguments (cf. 4.1.7 and 4.1.6). As a consequence, vv and nn end up in the top of the stack of the exception handler. The remainder of the old stack is discarded. Notice that if the continuation in c2\texttt{c2} has a value for c2\texttt{c2} in its savelist, it will be used to set up the new value of c2\texttt{c2} before executing the exception handler. In particular, if the exception handler invokes THROWANY\texttt{THROWANY}, it will re-throw the original exception with the restored value of c2\texttt{c2}. This trick enables the exception handler to handle only some exceptions, and pass the rest to an outer exception handler.

4.5.5. Default exception handler

When an instance of TVM is created, c2\texttt{c2} contains a reference to the “default exception handler continuation”, which is an ec_fatal\texttt{ec\_fatal} extraordinary continuation (cf. 4.1.5). Its execution leads to the termination of the execution of TVM, with the arguments vv and nn of the exception returned to the outside caller. In the context of the TON Blockchain, nn will be stored as a part of the transaction’s result.

4.5.6. TRY\texttt{TRY} primitive

A TRY\texttt{TRY} primitive can be used to implement C++- like exception handling. This primitive accepts two continuations, cc and cc'. It stores the old value of c2\texttt{c2} into the savelist of cc', sets c2\texttt{c2} to cc', and executes cc just as EXECUTE\texttt{EXECUTE} would, but additionally saving the old value of c2\texttt{c2} into the savelist of the new c0\texttt{c0} as well. Usually a version of the TRY\texttt{TRY} primitive with an explicit number of arguments nn'' passed to the continuation cc is used. The net result is roughly equivalent to C++++’s try\texttt{try} { cc } catch(...)\texttt{catch(...)} { cc' } operator.

4.5.7. List of predefined exceptions

Predefined exceptions of TVM correspond to exception numbers nn in the range 0–31. They include:
  • Normal termination (n=0n=0) — Should never be generated, but it is useful for some tricks.
  • Alternative termination (n=1n=1) — Again, should never be generated.
  • Stack underflow (n=2n=2) — Not enough arguments in the stack for a primitive.
  • Stack overflow (n=3n=3) — More values have been stored on a stack than allowed by this version of TVM.
  • Integer overflow (n=4n=4) — Integer does not fit into 2256x<2256-2^{256}\leq x<2^{256}, or a division by zero has occurred.
  • Range check error (n=5n=5) — Integer out of expected range.
  • Invalid opcode (n=6n=6) — Instruction or its immediate arguments cannot be decoded.
  • Type check error (n=7n=7) — An argument to a primitive is of incorrect value type.
  • Cell overflow (n=8n=8) — Error in one of the serialization primitives.
  • Cell underflow (n=9n=9) — Deserialization error.
  • Dictionary error (n=10n=10) — Error while deserializing a dictionary object.
  • Unknown error (n=11n=11) — Unknown error, may be thrown by user programs.
  • Fatal error (n=12n=12) — Thrown by TVM in situations deemed impossible.
  • Out of gas (n=13n=13) — Thrown by TVM when the remaining gas (grg_r) becomes negative. This exception usually cannot be caught and leads to an immediate termination of TVM.
Most of these exceptions have no parameter (i.e., use a zero integer instead). The order in which these exceptions are checked is outlined below in 4.5.8.

4.5.8. Order of stack underflow, type check, and range check exceptions

All TVM primitives first check whether the stack contains the required number of arguments, generating a stack underflow exception if this is not the case. Only then are the type tags of the arguments and their ranges (e.g., if a primitive expects an argument not only to be an Integer, but also to be in the range from 0 to 256) checked, starting from the value in the top of the stack (the last argument) and proceeding deeper into the stack. If an argument’s type is incorrect, a type-checking exception is generated; if the type is correct, but the value does not fall into the expected range, a range check exception is generated. Some primitives accept a variable number of arguments, depending on the values of some small fixed subset of arguments located near the top of the stack. In this case, the above procedure is first run for all arguments from this small subset. Then it is repeated for the remaining arguments, once their number and types have been determined from the arguments already processed.

4.6 Functions, recursion, and dictionaries

4.6.1. The problem of recursion

The conditional and iterated execution primitives described in 4.2—along with the unconditional branch, call, and return primitives described in 4.1—enable one to implement more or less arbitrary code with nested loops and conditional expressions, with one notable exception: one can only create new constant continuations from parts of the current continuation. (In particular, one cannot invoke a subroutine from itself in this way.) Therefore, the code being executed—i.e., the current continuation—gradually becomes smaller and smaller.24

4.6.2. Y-combinator solution: pass a continuation as an argument to itself

One way of dealing with the problem of recursion is by passing a copy of the continuation representing the body of a recursive function as an extra argument to itself. Consider, for example, the following code for a factorial function:
71      PUSHINT 1
9C      PUSHCONT {
22        PUSH s2
72        PUSHINT 2
B9        LESS
DC        IFRET
59        ROTREV
21        PUSH s1
A8        MUL
01        SWAP
A5        DEC
02        XCHG s2
20        DUP
D9        JMPX
        }
20      DUP
D8      EXECUTE
30      DROP
31      NIP
This roughly corresponds to defining an auxiliary function body\mathit{body} with three arguments nn, xx, and ff, such that body(n,x,f)\mathit{body}(n,x,f) equals xx if n<2n<2 and f(n1,nx,f)f(n-1,nx,f) otherwise, then invoking body(n,1,body)\mathit{body}(n,1,\mathit{body}) to compute the factorial of nn. The recursion is then implemented with the aid of the DUP\texttt{DUP}; EXECUTE\texttt{EXECUTE} construction, or DUP\texttt{DUP}; JMPX\texttt{JMPX} in the case of tail recursion. This trick is equivalent to applying YY-combinator to a function body\mathit{body}.

4.6.3. A variant of Y-combinator solution

Another way of recursively computing the factorial, more closely following the classical recursive definition fact(n):={1if n<2,nfact(n1)otherwise(5)\mathit{fact}(n):=\begin{cases}1&\quad\text{if }n<2,\\n\cdot\mathit{fact}(n-1)&\quad\text{otherwise}\end{cases} \tag{5} is as follows:
9D      PUSHCONT {
21        OVER
C102      LESSINT 2
92        PUSHCONT {
5B          2DROP
71          PUSHINT 1
          }
E0        IFJMP
21        OVER
A5        DEC
01        SWAP
20        DUP
D8        EXECUTE
A8        MUL
        }
20      DUP
D9      JMPX
This definition of the factorial function is two bytes shorter than the previous one, but it uses general recursion instead of tail recursion, so it cannot be easily transformed into a loop.

4.6.4. Comparison: non-recursive definition of the factorial function

Incidentally, a non-recursive definition of the factorial with the aid of a REPEAT\texttt{REPEAT} loop is also possible, and it is much shorter than both recursive definitions:
71      PUSHINT 1
01      SWAP
20      DUP
94      PUSHCONT {
66        TUCK
A8        MUL
01        SWAP
A5        DEC
        }
E4      REPEAT
30      DROP

4.6.5. Several mutually recursive functions

If one has a collection f1f_1, \ldots, fnf_n of mutually recursive functions, one can use the same trick by passing the whole collection of continuations {fi}\{f_i\} in the stack as an extra nn arguments to each of these functions. However, as nn grows, this becomes more and more cumbersome, since one has to reorder these extra arguments in the stack to work with the “true” arguments, and then push their copies into the top of the stack before any recursive call.

4.6.6. Combining several functions into one tuple

One might also combine a collection of continuations representing functions f1f_1, \ldots, fnf_n into a “tuple” f:=(f1,,fn){\mathbf f}:=(f_1,\ldots,f_n), and pass this tuple as one stack element f{\mathbf f}. For instance, when n4n\leq4, each function can be represented by a cell f~i\tilde f_i (along with the tree of cells rooted in this cell), and the tuple may be represented by a cell f~\tilde{\mathbf f}, which has references to its component cells f~i\tilde f_i. However, this would lead to the necessity of “unpacking” the needed component from this tuple before each recursive call.

4.6.7. Combining several functions into a selector function

Another approach is to combine several functions f1f_1, \ldots, fnf_n into one “selector function” ff, which takes an extra argument ii, 1in1\leq i\leq n, from the top of the stack, and invokes the appropriate function fif_i. Stack machines such as TVM are well-suited to this approach, because they do not require the functions fif_i to have the same number and types of arguments. Using this approach, one would need to pass only one extra argument, ff, to each of these functions, and push into the stack an extra argument ii before each recursive call to ff to select the correct function to be called.

4.6.8. Using a dedicated register to keep the selector function

However, even if we use one of the two previous approaches to combine all functions into one extra argument, passing this argument to all mutually recursive functions is still quite cumbersome and requires a lot of additional stack manipulation operations. Because this argument changes very rarely, one might use a dedicated register to keep it and transparently pass it to all functions called. This is the approach used by TVM by default.

4.6.9. Special register c3c3 for the selector function

In fact, TVM uses a dedicated register c3\texttt{c3} to keep the continuation representing the current or global “selector function”, which can be used to invoke any of a family of mutually recursive functions. Special primitives CALL\texttt{CALL} nnnn or CALLDICT\texttt{CALLDICT} nnnn (cf. A.8.7) are equivalent to PUSHINT\texttt{PUSHINT} nnnn; PUSH c3\texttt{PUSH c3}; EXECUTE\texttt{EXECUTE}, and similarly JMP\texttt{JMP} nnnn or JMPDICT\texttt{JMPDICT} nnnn are equivalent to PUSHINT\texttt{PUSHINT} nnnn; PUSH c3\texttt{PUSH c3}; JMPX\texttt{JMPX}. In this way a TVM program, which ultimately is a large collection of mutually recursive functions, may initialize c3\texttt{c3} with the correct selector function representing the family of all the functions in the program, and then use CALL\texttt{CALL} nnnn to invoke any of these functions by its index (sometimes also called the selector of a function).

4.6.10. Initialization of c3c3

A TVM program might initialize c3\texttt{c3} by means of a POP c3\texttt{POP c3} instruction. However, because this usually is the very first action undertaken by a program (e.g., a smart contract), TVM makes some provisions for the automatic initialization of c3\texttt{c3}. Namely, c3\texttt{c3} is initialized by the code (the initial value of cc\texttt{cc}) of the program itself, and an extra zero (or, in some cases, some other predefined number ss) is pushed into the stack before the program’s execution. This is approximately equivalent to invoking JMPDICT 0\texttt{JMPDICT 0} (or JMPDICT\texttt{JMPDICT} ss) at the very beginning of a program—i.e., the function with index zero is effectively the main() function for the program.

4.6.11. Creating selector functions and switch\texttt{switch} statements

TVM makes special provisions for simple and concise implementation of selector functions (which usually constitute the top level of a TVM program) or, more generally, arbitrary switch or case statements (which are also useful in TVM programs). The most important primitives included for this purpose are IFBITJMP\texttt{IFBITJMP}, IFNBITJMP\texttt{IFNBITJMP}, IFBITJMPREF\texttt{IFBITJMPREF}, and IFNBITJMPREF\texttt{IFNBITJMPREF} (cf. A.8.2). They effectively enable one to combine subroutines, kept either in separate cells or as subslices of certain cells, into a binary decision tree with decisions made according to the indicated bits of the integer passed in the top of the stack. Another instruction, useful for the implementation of sum-product types, is PLDUZ\texttt{PLDUZ} (cf. A.7.2). This instruction preloads the first several bits of a Slice into an Integer, which can later be inspected by IFBITJMP\texttt{IFBITJMP} and other similar instructions.

4.6.12. Alternative: using a hashmap to select the correct function

Yet another alternative is to use a Hashmap (cf. 3.3) to hold the “collection” or “dictionary” of the code of all functions in a program, and use the hashmap lookup primitives (cf. A.10) to select the code of the required function, which can then be BLESS\texttt{BLESS}ed into a continuation (cf. A.8.5) and executed. Special combined “lookup, bless, and execute” primitives, such as DICTIGETJMP\texttt{DICTIGETJMP} and DICTIGETEXEC\texttt{DICTIGETEXEC}, are also available (cf. A.10.11). This approach may be more efficient for larger programs and switch statements.

5. Codepages and instruction encoding

This chapter describes the codepage mechanism, which allows TVM to be flexible and extendable while preserving backward compatibility with respect to previously generated code. We also discuss some general considerations about instruction encodings (applicable to arbitrary machine code, not just TVM), as well as the implications of these considerations for TVM and the choices made while designing TVM’s (experimental) codepage zero. The instruction encodings themselves are presented later in Appendix A.

5.1 Codepages and interoperability of different TVM versions

The codepages are an essential mechanism of backward compatibility and of future extensions to TVM. They enable transparent execution of code written for different revisions of TVM, with transparent interaction between instances of such code. The mechanism of the codepages, however, is general and powerful enough to enable some other originally unintended applications.

5.1.1. Codepages in continuations

Every ordinary continuation contains a 16-bit codepage field cp\texttt{cp} (cf. 4.1.1), which determines the codepage that will be used to execute its code. If a continuation is created by a PUSHCONT\texttt{PUSHCONT} (cf. 4.2.3) or similar primitive, it usually inherits the current codepage (i.e., the codepage of cc\texttt{cc}).25

5.1.2. Current codepage

The current codepage cp\texttt{cp} (cf. 1.4) is the codepage of the current continuation cc\texttt{cc}. It determines the way the next instruction will be decoded from cc.code\texttt{cc.code}, the remainder of the current continuation’s code. Once the instruction has been decoded and executed, it determines the next value of the current codepage. In most cases, the current codepage is left unchanged. On the other hand, all primitives that switch the current continuation load the new value of cp\texttt{cp} from the new current continuation. In this way, all code in continuations is always interpreted exactly as it was intended to be.

5.1.3. Different versions of TVM may use different codepages

Different versions of TVM may use different codepages for their code. For example, the original version of TVM might use codepage zero. A newer version might use codepage one, which contains all the previously defined opcodes, along with some newly defined ones, using some of the previously unused opcode space. A subsequent version might use yet another codepage, and so on. However, a newer version of TVM will execute old code for codepage zero exactly as before. If the old code contained an opcode used for some new operations that were undefined in the original version of TVM, it will still generate an invalid opcode exception, because the new operations are absent in codepage zero.

5.1.4. Changing the behavior of old operations

New codepages can also change the effects of some operations present in the old codepages while preserving their opcodes and mnemonics. For example, imagine a future 513-bit upgrade of TVM (replacing the current 257-bit design). It might use a 513-bit Integer type within the same arithmetic primitives as before. However, while the opcodes and instructions in the new codepage would look exactly like the old ones, they would work differently, accepting 513-bit integer arguments and results. On the other hand, during the execution of the same code in codepage zero, the new machine would generate exceptions whenever the integers used in arithmetic and other primitives do not fit into 257 bits.26 In this way, the upgrade would not change the behavior of the old code.

5.1.5. Improving instruction encoding

Another application for codepages is to change instruction encodings, reflecting improved knowledge of the actual frequencies of such instructions in the code base. In this case, the new codepage will have exactly the same instructions as the old one, but with different encodings, potentially of differing lengths. For example, one might create an experimental version of the first version of TVM, using a (prefix) bitcode instead of the original bytecode, aiming to achieve higher code density.

5.1.6. Making instruction encoding context-dependent

Another way of using codepages to improve code density is to use several codepages with different subsets of the whole instruction set defined in each of them, or with the whole instruction set defined, but with different length encodings for the same instructions in different codepages. Imagine, for instance, a “stack manipulation” codepage, where stack manipulation primitives have short encodings at the expense of all other operations, and a “data processing” codepage, where all other operations are shorter at the expense of stack manipulation operations. If stack manipulation operations tend to come one after another, we can automatically switch to “stack manipulation” codepage after executing any such instruction. When a data processing instruction occurs, we switch back to “data processing” codepage. If conditional probabilities of the class of the next instruction depending on the class of the previous instruction are considerably different from corresponding unconditional probabilities, this technique—automatically switching into stack manipulation mode to rearrange the stack with shorter instructions, then switching back—might considerably improve the code density.

5.1.7. Using codepages for status and control flags

Another potential application of multiple codepages inside the same revision of TVM consists in switching between several codepages depending on the result of the execution of some instructions. For example, imagine a version of TVM that uses two new codepages, 2 and 3. Most operations do not change the current codepage. However, the integer comparison operations will switch to codepage 2 if the condition is false, and to codepage 3 if it is true. Furthermore, a new operation ?EXECUTE\texttt{?EXECUTE}, similar to EXECUTE\texttt{EXECUTE}, will indeed be equivalent to EXECUTE\texttt{EXECUTE} in codepage 3, but will instead be a DROP\texttt{DROP} in codepage 2. Such a trick effectively uses bit 0 of the current codepage as a status flag. Alternatively, one might create a couple of codepages—say, 4 and 5—which differ only in their cell deserialisation primitives. For instance, in codepage 4 they might work as before, while in codepage 5 they might deserialize data not from the beginning of a Slice, but from its end. Two new instructions—say, CLD\texttt{CLD} and STD\texttt{STD}—might be used for switching to codepage 4 or codepage 5. Clearly, we have now described a status flag, affecting the execution of some instructions in a certain new manner.

5.1.8. Setting the codepage in the code itself

For convenience, we reserve some opcode in all codepages—say, FF\texttt{FF} nn—for the instruction SETCP\texttt{SETCP} nn, with nn from 0 to 255 (cf. A.13). Then by inserting such an instruction into the very beginning of (the main function of) a program (e.g., a TON Blockchain smart contract) or a library function, we can ensure that the code will always be executed in the intended codepage.

5.2 Instruction encoding

This section discusses the general principles of instruction encoding valid for all codepages and all versions of TVM. Later, 5.3 discusses the choices made for the experimental “codepage zero”.

5.2.1. Instructions are encoded by a binary prefix code

All complete instructions (i.e., instructions along with all their parameters, such as the names of stack registers s(i)s(i) or other embedded constants) of a TVM codepage are encoded by a binary prefix code. This means that a (finite) binary string (i.e., a bitstring) corresponds to each complete instruction, in such a way that binary strings corresponding to different complete instructions do not coincide, and no binary string among the chosen subset is a prefix of another binary string from this subset.

5.2.2. Determining the first instruction from a code stream

As a consequence of this encoding method, any binary string admits at most one prefix, which is an encoding of some complete instruction. In particular, the code cc.code\texttt{cc.code} of the current continuation (which is a Slice, and thus a bitstring along with some cell references) admits at most one such prefix, which corresponds to the (uniquely determined) instruction that TVM will execute first. After execution, this prefix is removed from the code of the current continuation, and the next instruction can be decoded.

5.2.3. Invalid opcode

If no prefix of cc.code\texttt{cc.code} encodes a valid instruction in the current codepage, an invalid opcode exception is generated (cf. 4.5.7). However, the case of an empty cc.code\texttt{cc.code} is treated separately as explained in 4.1.4 (the exact behavior may depend on the current codepage).

5.2.4. Special case: end-of-code padding

As an exception to the above rule, some codepages may accept some values of cc.code\texttt{cc.code} that are too short to be valid instruction encodings as additional variants of NOP\texttt{NOP}, thus effectively using the same procedure for them as for an empty cc.code\texttt{cc.code}. Such bitstrings may be used for padding the code near its end. For example, if binary string 00000000\texttt{00000000} (i.e., x00\texttt{x00}, cf. 1.0.3) is used in a codepage to encode NOP\texttt{NOP}, its proper prefixes cannot encode any instructions. So this codepage may accept 0\texttt{0}, 00\texttt{00}, 000\texttt{000}, \ldots, 0000000\texttt{0000000} as variants of NOP\texttt{NOP} if this is all that is left in cc.code\texttt{cc.code}, instead of generating an invalid opcode exception. Such a padding may be useful, for example, if the PUSHCONT\texttt{PUSHCONT} primitive (cf. 4.2.3) creates only continuations with code consisting of an integral number of bytes, but not all instructions are encoded by an integral number of bytes.

5.2.5. TVM code is a bitcode, not a bytecode

Recall that TVM is a bit-oriented machine in the sense that its Cells (and Slices) are naturally considered as sequences of bits, not just of octets (bytes), cf. 3.2.5. Because the TVM code is also kept in cells (cf. 3.1.9 and 4.1.4), there is no reason to use only bitstrings of length divisible by eight as encodings of complete instructions. In other words, generally speaking, the TVM code is a bitcode, not a bytecode. That said, some codepages (such as our experimental codepage zero) may opt to use a bytecode (i.e., to use only encodings consisting of an integral number of bytes)—either for simplicity, or for the ease of debugging and of studying memory (i.e., cell) dumps.27

5.2.6. Opcode space used by a complete instruction

Recall from coding theory that the lengths of bitstrings lil_i used in a binary prefix code satisfy Kraft–McMillan inequality i2li1\sum_i2^{-l_i}\leq1. This is applicable in particular to the (complete) instruction encoding used by a TVM codepage. We say that a particular complete instruction (or, more precisely, the encoding of a complete instruction) utilizes the portion 2l2^{-l} of the opcode space, if it is encoded by an ll-bit string. One can see that all complete instructions together utilize at most 11 (i.e., “at most the whole opcode space”).

5.2.7. Opcode space used by an instruction, or a class of instructions

The above terminology is extended to instructions (considered with all admissible values of their parameters), or even classes of instructions (e.g., all arithmetic instructions). We say that an (incomplete) instruction, or a class of instructions, occupies portion α\alpha of the opcode space, if α\alpha is the sum of the portions of the opcode space occupied by all complete instructions belonging to that class.

5.2.8. Opcode space for bytecodes

A useful approximation of the above definitions is as follows: Consider all 256 possible values for the first byte of an instruction encoding. Suppose that kk of these values correspond to the specific instruction or class of instructions we are considering. Then this instruction or class of instructions occupies approximately the portion k/256k/256 of the opcode space. This approximation shows why all instructions cannot occupy together more than the portion 256/256=1256/256=1 of the opcode space, at least without compromising the uniqueness of instruction decoding.

5.2.9. Almost optimal encodings

Coding theory tells us that in an optimally dense encoding, the portion of the opcode space used by a complete instruction (2l2^{-l}, if the complete instruction is encoded in ll bits) should be approximately equal to the probability or frequency of its occurrence in real programs.28 The same should hold for (incomplete) instructions, or primitives (i.e., generic instructions without specified values of parameters), and for classes of instructions.

5.2.10. Example: stack manipulation primitives

For instance, if stack manipulation instructions constitute approximately half of all instructions in a typical TVM program, one should allocate approximately half of the opcode space for encoding stack manipulation instructions. One might reserve the first bytes (“opcodes”) 0x00\texttt{0x00}-0x7f\texttt{0x7f} for such instructions. If a quarter of these instructions are XCHG\texttt{XCHG}, it would make sense to reserve 0x00\texttt{0x00}-0x1f\texttt{0x1f} for XCHG\texttt{XCHG}s. Similarly, if half of all XCHG\texttt{XCHG}s involve the top of stack s0\texttt{s0}, it would make sense to use 0x00\texttt{0x00}-0x0f\texttt{0x0f} to encode XCHG s0,s(i)\texttt{XCHG s0,s}(i).

5.2.11. Simple encodings of instructions

In most cases, simple encodings of complete instructions are used. Simple encodings begin with a fixed bitstring called the opcode of the instruction, followed by, say, 4-bit fields containing the indices ii of stack registers s(i)\texttt{s}(i) specified in the instruction, followed by all other constant (literal, immediate) parameters included in the complete instruction. While simple encodings may not be exactly optimal, they admit short descriptions, and their decoding and encoding can be easily implemented. If a (generic) instruction uses a simple encoding with an ll-bit opcode, then the instruction will utilize 2l2^{-l} portion of the opcode space. This observation might be useful for considerations described in 5.2.9 and 5.2.10.

5.2.12. Optimizing code density further: Huffman codes

One might construct optimally dense binary code for the set of all complete instructions, provided their probabilities or frequences in real code are known. This is the well-known Huffman code (for the given probability distribution). However, such code would be highly unsystematic and hard to decode.

5.2.13. Practical instruction encodings

In practice, instruction encodings used in TVM and other virtual machines offer a compromise between code density and ease of encoding and decoding. Such a compromise may be achieved by selecting simple encodings (cf. 5.2.11) for all instructions (maybe with separate simple encodings for some often used variants, such as XCHG s0,s(i)\texttt{XCHG s0,s}(i) among all XCHG s(i),s(j)\texttt{XCHG s}(i)\texttt{,s}(j)), and allocating opcode space for such simple encodings using the heuristics outlined in 5.2.9 and 5.2.10; this is the approach currently used in TVM.

5.3 Instruction encoding in codepage zero

This section provides details about the experimental instruction encoding for codepage zero, as described elsewhere in this document (cf. Appendix A) and used in the preliminary test version of TVM.

5.3.1. Upgradability

First of all, even if this preliminary version somehow gets into the production version of the TON Blockchain, the codepage mechanism (cf. 5.1) enables us to introduce better versions later without compromising backward compatibility.29 So in the meantime, we are free to experiment.

5.3.2. Choice of instructions

We opted to include many “experimental” and not strictly necessary instructions in codepage zero just to see how they might be used in real code. For example, we have both the basic (cf. 2.2.1) and the compound (cf. 2.2.3) stack manipulation primitives, as well as some “unsystematic” ones such as ROT\texttt{ROT} (mostly borrowed from Forth). If such primitives are rarely used, their inclusion just wastes some part of the opcode space and makes the encodings of other instructions slightly less effective, something we can afford at this stage of TVM’s development.

5.3.3. Using experimental instructions

Some of these experimental instructions have been assigned quite long opcodes, just to fit more of them into the opcode space. One should not be afraid to use them just because they are long; if these instructions turn out to be useful, they will receive shorter opcodes in future revisions. Codepage zero is not meant to be fine-tuned in this respect.

5.3.4. Choice of bytecode

We opted to use a bytecode (i.e., to use encodings of complete instructions of lengths divisible by eight). While this may not produce optimal code density, because such a length restriction makes it more difficult to match portions of opcode space used for the encoding of instructions with estimated frequencies of these instructions in TVM code (cf. 5.2.11 and 5.2.9), such an approach has its advantages: it admits a simpler instruction decoder and simplifies debugging (cf. 5.2.5). After all, we do not have enough data on the relative frequencies of different instructions right now, so our code density optimizations are likely to be very approximate at this stage. The ease of debugging and experimenting and the simplicity of implementation are more important at this point.

5.3.5. Simple encodings for all instructions

For similar reasons, we opted to use simple encodings for all instructions (cf. 5.2.11 and 5.2.13), with separate simple encodings for some very frequently used subcases as outlined in 5.2.13. That said, we tried to distribute opcode space using the heuristics described in 5.2.9 and 5.2.10.

5.3.6. Lack of context-dependent encodings

This version of TVM also does not use context-dependent encodings (cf. 5.1.6). They may be added at a later stage, if deemed useful.

5.3.7. The list of all instructions

The list of all instructions available in codepage zero, along with their encodings and (in some cases) short descriptions, may be found in Appendix A.

A Instructions and opcodes

This appendix lists all instructions available in the (experimental) codepage zero of TVM, as explained in 5.3. We list the instructions in lexicographical opcode order. However, the opcode space is distributed in such way as to make all instructions in each category (e.g., arithmetic primitives) have neighboring opcodes. So we first list a number of stack manipulation primitives, then constant primitives, arithmetic primitives, comparison primitives, cell primitives, continuation primitives, dictionary primitives, and finally application-specific primitives. We use hexadecimal notation (cf. 1.0) for bitstrings. Stack registers s(i)\texttt{s}(i) usually have 0i150\leq i\leq 15, and ii is encoded in a 4-bit field (or, on a few rare occasions, in an 8-bit field). Other immediate parameters are usually 4-bit, 8-bit, or variable length. The stack notation described in 2.1.10 is extensively used throughout this appendix.

A.1 Gas prices

The gas price for most primitives equals the basic gas price, computed as Pb:=10+b+5rP_b:=10+b+5r, where bb is the instruction length in bits and rr is the number of cell references included in the instruction. When the gas price of an instruction differs from this basic price, it is indicated in parentheses after its mnemonics, either as (xx), meaning that the total gas price equals xx, or as (+xx), meaning Pb+xP_b+x. Apart from integer constants, the following expressions may appear:
  • CrC_r — The total price of “reading” cells (i.e., transforming cell references into cell slices). Currently equal to 100 or 25 gas units per cell depending on whether it is the first time a cell with this hash is being “read” during the current run of the VM or not.
  • LL — The total price of loading cells. Depends on the loading action required.
  • BwB_w — The total price of creating new Builders. Currently equal to 0 gas units per builder.
  • CwC_w — The total price of creating new Cells from Builders. Currently equal to 500 gas units per cell.
By default, the gas price of an instruction equals P:=Pb+Cr+L+Bw+CwP:=P_b+C_r+L+B_w+C_w.

A.2 Stack manipulation primitives

This section includes both the basic (cf. 2.2.1) and the compound (cf. 2.2.3) stack manipulation primitives, as well as some “unsystematic” ones. Some compound stack manipulation primitives, such as XCPU\texttt{XCPU} or XCHG2\texttt{XCHG2}, turn out to have the same length as an equivalent sequence of simpler operations. We have included these primitives regardless, so that they can easily be allocated shorter opcodes in a future revision of TVM—or removed for good. Some stack manipulation instructions have two mnemonics: one Forth-style (e.g., -ROT\texttt{-ROT}), the other conforming to the usual rules for identifiers (e.g., ROTREV\texttt{ROTREV}). Whenever a stack manipulation primitive (e.g., PICK\texttt{PICK}) accepts an integer parameter nn from the stack, it must be within the range 02550\ldots255; otherwise a range check exception happens before any further checks.

A.2 Stack manipulation primitives

A.2.1. Basic stack manipulation primitives

  • 00\texttt{00}NOP\texttt{NOP}, does nothing.
  • 01\texttt{01}XCHG s1\texttt{XCHG s1}, also known as SWAP\texttt{SWAP}.
  • 0i\texttt{0}iXCHG s(i)\texttt{XCHG s}(i) or XCHG s0,s(i)\texttt{XCHG s0,s}(i), interchanges the top of the stack with s(i)\texttt{s}(i), 1i151\leq i\leq 15.
  • 10ij\texttt{10}ijXCHG s(i),s(j)\texttt{XCHG s}(i)\texttt{,s}(j), 1i<j151\leq i<j\leq15, interchanges s(i)\texttt{s}(i) with s(j)\texttt{s}(j).
  • 11ii\texttt{11}iiXCHG s0,s(ii)\texttt{XCHG s0,s}(ii), with 0ii2550\leq ii\leq255.
  • 1i\texttt{1}iXCHG s1,s(i)\texttt{XCHG s1,s}(i), 2i152\leq i\leq 15.
  • 2i\texttt{2}iPUSH s(i)\texttt{PUSH s}(i), 0i150\leq i\leq 15, pushes a copy of the old s(i)\texttt{s}(i) into the stack.
  • 20\texttt{20}PUSH s0\texttt{PUSH s0}, also known as DUP\texttt{DUP}.
  • 21\texttt{21}PUSH s1\texttt{PUSH s1}, also known as OVER\texttt{OVER}.
  • 3i\texttt{3}iPOP s(i)\texttt{POP s}(i), 0i150\leq i\leq 15, pops the old top-of-stack value into the old s(i)\texttt{s}(i).
  • 30\texttt{30}POP s0\texttt{POP s0}, also known as DROP\texttt{DROP}, discards the top-of-stack value.
  • 31\texttt{31}POP s1\texttt{POP s1}, also known as NIP\texttt{NIP}.

A.2.2. Compound stack manipulation primitives

Parameters ii, jj, and kk of the following primitives all are 4-bit integers in the range 0150\ldots15.
  • 4ijk\texttt{4}ijkXCHG3 s(i),s(j),s(k)\texttt{XCHG3 s}(i)\texttt{,s}(j)\texttt{,s}(k), equivalent to XCHG s2,s(i)\texttt{XCHG s2,s}(i); XCHG s1, s(j)\texttt{XCHG s1, s}(j); XCHG s0,s(k)\texttt{XCHG s0,s}(k), with 0i,j,k150\leq i,j,k\leq 15.
  • 50ij\texttt{50}ijXCHG2 s(i),s(j)\texttt{XCHG2 s}(i)\texttt{,s}(j), equivalent to XCHG s1,s(i)\texttt{XCHG s1,s}(i); XCHG s(j)\texttt{XCHG s}(j).
  • 51ij\texttt{51}ijXCPU s(i),s(j)\texttt{XCPU s}(i)\texttt{,s}(j), equivalent to XCHG s(i)\texttt{XCHG s}(i); PUSH s(j)\texttt{PUSH s}(j).
  • 52ij\texttt{52}ijPUXC s(i),s(j1)\texttt{PUXC s}(i)\texttt{,s}(j-1), equivalent to PUSH s(i)\texttt{PUSH s}(i); SWAP\texttt{SWAP}; XCHG s(j)\texttt{XCHG s}(j).
  • 53ij\texttt{53}ijPUSH2 s(i),s(j)\texttt{PUSH2 s}(i)\texttt{,s}(j), equivalent to PUSH s(i)\texttt{PUSH s}(i); PUSH s(j+1)\texttt{PUSH s}(j+1).
  • 540ijk\texttt{540}ijkXCHG3 s(i),s(j),s(k)\texttt{XCHG3 s}(i)\texttt{,s}(j)\texttt{,s}(k) (long form).
  • 541ijk\texttt{541}ijkXC2PU s(i),s(j),s(k)\texttt{XC2PU s}(i)\texttt{,s}(j)\texttt{,s}(k), equivalent to XCHG2 s(i),s(j)\texttt{XCHG2 s}(i)\texttt{,s}(j); PUSH s(k)\texttt{PUSH s}(k).
  • 542ijk\texttt{542}ijkXCPUXC s(i),s(j),s(k1)\texttt{XCPUXC s}(i)\texttt{,s}(j)\texttt{,s}(k-1), equivalent to XCHG s1,s(i)\texttt{XCHG s1,s}(i); PUXC s(j),s(k1)\texttt{PUXC s}(j)\texttt{,s}(k-1).
  • 543ijk\texttt{543}ijkXCPU2 s(i),s(j),s(k)\texttt{XCPU2 s}(i)\texttt{,s}(j)\texttt{,s}(k), equivalent to XCHG s(i)\texttt{XCHG s}(i); PUSH2 s(j), s(k)\texttt{PUSH2 s}(j)\texttt{, s}(k).
  • 544ijk\texttt{544}ijkPUXC2 s(i),s(j1),s(k1)\texttt{PUXC2 s}(i)\texttt{,s}(j-1)\texttt{,s}(k-1), equivalent to PUSH s(i)\texttt{PUSH s}(i); XCHG s2\texttt{XCHG s2}; XCHG2 s(j),s(k)\texttt{XCHG2 s}(j)\texttt{,s}(k).
  • 545ijk\texttt{545}ijkPUXCPU s(i),s(j1),s(k1)\texttt{PUXCPU s}(i)\texttt{,s}(j-1)\texttt{,s}(k-1), equivalent to PUXC s(i),s(j1)\texttt{PUXC s}(i)\texttt{,s}(j-1); PUSH s(k)\texttt{PUSH s}(k).
  • 546ijk\texttt{546}ijkPU2XC s(i),s(j1),s(k2)\texttt{PU2XC s}(i)\texttt{,s}(j-1)\texttt{,s}(k-2), equivalent to PUSH s(i)\texttt{PUSH s}(i); SWAP\texttt{SWAP}; PUXC s(j),s(k1)\texttt{PUXC s}(j)\texttt{,s}(k-1).
  • 547ijk\texttt{547}ijkPUSH3 s(i),s(j),s(k)\texttt{PUSH3 s}(i)\texttt{,s}(j)\texttt{,s}(k), equivalent to PUSH s(i)\texttt{PUSH s}(i); PUSH2 s(j+1),s(k+1)\texttt{PUSH2 s}(j+1)\texttt{,s}(k+1).
  • 54C_\texttt{54C\_} — unused.

A.2.3. Exotic stack manipulation primitives

  • 55ij\texttt{55}ijBLKSWAP\texttt{BLKSWAP} i+1i+1,j+1j+1, permutes two blocks s(j+i+1)s(j+1)\texttt{s}(j+i+1)\ldots\texttt{s}(j+1) and s(j)s0\texttt{s}(j)\ldots\texttt{s0}, for 0i,j150\leq i,j\leq15. Equivalent to REVERSE\texttt{REVERSE} i+1i+1,j+1j+1; REVERSE\texttt{REVERSE} j+1j+1,0; REVERSE\texttt{REVERSE} i+j+2i+j+2,0.
  • 5513\texttt{5513}ROT2\texttt{ROT2} or 2ROT\texttt{2ROT} (aa bb cc dd ee ffcc dd ee ff aa bb), rotates the three topmost pairs of stack entries.
  • 550i\texttt{550}iROLL\texttt{ROLL} i+1i+1, rotates the top i+1i+1 stack entries. Equivalent to BLKSWAP 1\texttt{BLKSWAP 1},i+1i+1.
  • 55i0\texttt{55}i\texttt{0}ROLLREV\texttt{ROLLREV} i+1i+1 or -ROLL\texttt{-ROLL} i+1i+1, rotates the top i+1i+1 stack entries in the other direction. Equivalent to BLKSWAP\texttt{BLKSWAP} i+1i+1,1.
  • 56ii\texttt{56}iiPUSH s(ii)\texttt{PUSH s}(ii) for 0ii2550\leq ii\leq 255.
  • 57ii\texttt{57}iiPOP s(ii)\texttt{POP s}(ii) for 0ii2550\leq ii\leq 255.
  • 58\texttt{58}ROT\texttt{ROT} (aa bb ccbb cc aa), equivalent to BLKSWAP 1,2\texttt{BLKSWAP 1,2} or to XCHG2 s2,s1\texttt{XCHG2 s2,s1}.
  • 59\texttt{59}ROTREV\texttt{ROTREV} or -ROT\texttt{-ROT} (aa bb cccc aa bb), equivalent to BLKSWAP 2,1\texttt{BLKSWAP 2,1} or to XCHG2 s2,s2\texttt{XCHG2 s2,s2}.
  • 5A\texttt{5A}SWAP2\texttt{SWAP2} or 2SWAP\texttt{2SWAP} (aa bb cc ddcc dd aa bb), equivalent to BLKSWAP 2,2\texttt{BLKSWAP 2,2} or to XCHG2 s3,s2\texttt{XCHG2 s3,s2}.
  • 5B\texttt{5B}DROP2\texttt{DROP2} or 2DROP\texttt{2DROP} (aa bb — ), equivalent to DROP\texttt{DROP}; DROP\texttt{DROP}.
  • 5C\texttt{5C}DUP2\texttt{DUP2} or 2DUP\texttt{2DUP} (aa bbaa bb aa bb), equivalent to PUSH2 s1,s0\texttt{PUSH2 s1,s0}.
  • 5D\texttt{5D}OVER2\texttt{OVER2} or 2OVER\texttt{2OVER} (aa bb cc ddaa bb cc dd aa bb), equivalent to PUSH2 s3,s2\texttt{PUSH2 s3,s2}.
  • 5Eij\texttt{5E}ijREVERSE\texttt{REVERSE} i+2i+2,jj, reverses the order of s(j+i+1)s(j)\texttt{s}(j+i+1)\ldots\texttt{s}(j) for 0i,j150\leq i,j\leq 15; equivalent to a sequence of i/2+1\lfloor i/2\rfloor+1 XCHG\texttt{XCHG}s.
  • 5F0i\texttt{5F0}iBLKDROP\texttt{BLKDROP} ii, equivalent to DROP\texttt{DROP} performed ii times.
  • 5Fij\texttt{5F}ijBLKPUSH\texttt{BLKPUSH} ii,jj, equivalent to PUSH s(j)\texttt{PUSH s}(j) performed ii times, 1i151\leq i\leq 15, 0j150\leq j\leq 15.
  • 60\texttt{60}PICK\texttt{PICK} or PUSHX\texttt{PUSHX}, pops integer ii from the stack, then performs PUSH s(i)\texttt{PUSH s}(i).
  • 61\texttt{61}ROLLX\texttt{ROLLX}, pops integer ii from the stack, then performs BLKSWAP 1\texttt{BLKSWAP 1},ii.
  • 62\texttt{62}-ROLLX\texttt{-ROLLX} or ROLLREVX\texttt{ROLLREVX}, pops integer ii from the stack, then performs BLKSWAP\texttt{BLKSWAP} ii,1.
  • 63\texttt{63}BLKSWX\texttt{BLKSWX}, pops integers ii,jj from the stack, then performs BLKSWAP\texttt{BLKSWAP} ii,jj.
  • 64\texttt{64}REVX\texttt{REVX}, pops integers ii,jj from the stack, then performs REVERSE\texttt{REVERSE} ii,jj.
  • 65\texttt{65}DROPX\texttt{DROPX}, pops integer ii from the stack, then performs BLKDROP\texttt{BLKDROP} ii.
  • 66\texttt{66}TUCK\texttt{TUCK} (aa bb - bb aa bb), equivalent to SWAP\texttt{SWAP}; OVER\texttt{OVER} or to XCPU s1,s1\texttt{XCPU s1,s1}.
  • 67\texttt{67}XCHGX\texttt{XCHGX}, pops integer ii from the stack, then performs XCHG s(i)\texttt{XCHG s}(i).
  • 68\texttt{68}DEPTH\texttt{DEPTH}, pushes the current depth of the stack.
  • 69\texttt{69}CHKDEPTH\texttt{CHKDEPTH}, pops integer ii from the stack, then checks whether there are at least ii elements, generating a stack underflow exception otherwise.
  • 6A\texttt{6A}ONLYTOPX\texttt{ONLYTOPX}, pops integer ii from the stack, then removes all but the top ii elements.
  • 6B\texttt{6B}ONLYX\texttt{ONLYX}, pops integer ii from the stack, then leaves only the bottom ii elements. Approximately equivalent to DEPTH\texttt{DEPTH}; SWAP\texttt{SWAP}; SUB\texttt{SUB}; DROPX\texttt{DROPX}.
  • 6C00\texttt{6C00}6C0F\texttt{6C0F} — reserved for stack operations.
  • 6Cij\texttt{6C}ijBLKDROP2\texttt{BLKDROP2} ii,jj, drops ii stack elements under the top jj elements, where 1i151\leq i\leq15 and 0j150\leq j\leq 15. Equivalent to REVERSE\texttt{REVERSE} i+ji+j,0; BLKDROP\texttt{BLKDROP} ii; REVERSE\texttt{REVERSE} jj,0.

A.3 Tuple, List, and Null primitives

Tuples are ordered collections consisting of at most 255 TVM stack values of arbitrary types (not necessarily the same). Tuple primitives create, modify, and unpack Tuples; they manipulate values of arbitrary types in the process, similarly to the stack primitives. We do not recommend using Tuples of more than 15 elements. When a Tuple tt contains elements x1x_1, …, xnx_n (in that order), we write t=(x1,,xn)t=(x_1,\ldots,x_n); number n0n\geq0 is the length of Tuple tt. It is also denoted by t|t|. Tuples of length two are called pairs, and Tuples of length three are triples. Lisp-style lists are represented with the aid of pairs, i.e., tuples consisting of exactly two elements. An empty list is represented by a Null value, and a non-empty list is represented by pair (h,t)(h,t), where hh is the first element of the list, and tt is its tail.

A.3.1. Null primitives

The following primitives work with (the only) value \bot of type Null, useful for representing empty lists, empty branches of binary trees, and absence of values in Maybe XX types. An empty Tuple created by NIL\texttt{NIL} could have been used for the same purpose; however, Null is more efficient and costs less gas.
  • 6D\texttt{6D}NULL\texttt{NULL} or PUSHNULL\texttt{PUSHNULL} ( — \bot), pushes the only value of type Null.
  • 6E\texttt{6E}ISNULL\texttt{ISNULL} (xx??), checks whether xx is a Null, and returns 1-1 or 00 accordingly.

A.3.2. Tuple primitives

  • 6F0n\texttt{6F0}nTUPLE\texttt{TUPLE} nn (x1x_1 \ldots xnx_ntt), creates a new Tuple t=(x1,,xn)t=(x_1,\ldots,x_n) containing nn values x1x_1, \ldots, xnx_n, where 0n150\leq n\leq 15.
  • 6F00\texttt{6F00}NIL\texttt{NIL} ( — tt), pushes the only Tuple t=()t=() of length zero.
  • 6F01\texttt{6F01}SINGLE\texttt{SINGLE} (xxtt), creates a singleton t:=(x)t:=(x), i.e., a Tuple of length one.
  • 6F02\texttt{6F02}PAIR\texttt{PAIR} or CONS\texttt{CONS} (xx yytt), creates pair t:=(x,y)t:=(x,y).
  • 6F03\texttt{6F03}TRIPLE\texttt{TRIPLE} (xx yy zztt), creates triple t:=(x,y,z)t:=(x,y,z).
  • 6F1k\texttt{6F1}kINDEX\texttt{INDEX} kk (ttxx), returns the kk-th element of a Tuple tt, where 0k150\leq k\leq 15. In other words, returns xk+1x_{k+1} if t=(x1,,xn)t=(x_1,\ldots,x_n). If knk\geq n, throws a range check exception.
  • 6F10\texttt{6F10}FIRST\texttt{FIRST} or CAR\texttt{CAR} (ttxx), returns the first element of a Tuple.
  • 6F11\texttt{6F11}SECOND\texttt{SECOND} or CDR\texttt{CDR} (ttyy), returns the second element of a Tuple.
  • 6F12\texttt{6F12}THIRD\texttt{THIRD} (ttzz), returns the third element of a Tuple.
  • 6F2n\texttt{6F2}nUNTUPLE\texttt{UNTUPLE} nn (ttx1x_1 \ldots xnx_n), unpacks a Tuple t=(x1,,xn)t=(x_1,\ldots,x_n) of length equal to 0n150\leq n\leq 15. If tt is not a Tuple, of if tn|t|\neq n, a type check exception is thrown.
  • 6F21\texttt{6F21}UNSINGLE\texttt{UNSINGLE} (ttxx), unpacks a singleton t=(x)t=(x).
  • 6F22\texttt{6F22}UNPAIR\texttt{UNPAIR} or UNCONS\texttt{UNCONS} (ttxx yy), unpacks a pair t=(x,y)t=(x,y).
  • 6F23\texttt{6F23}UNTRIPLE\texttt{UNTRIPLE} (ttxx yy zz), unpacks a triple t=(x,y,z)t=(x,y,z).
  • 6F3k\texttt{6F3}kUNPACKFIRST\texttt{UNPACKFIRST} kk (ttx1x_1 \ldots xkx_k), unpacks first 0k150\leq k\leq 15 elements of a Tuple tt. If t<k|t|<k, throws a type check exception.
  • 6F30\texttt{6F30}CHKTUPLE\texttt{CHKTUPLE} (tt — ), checks whether tt is a Tuple.
  • 6F4n\texttt{6F4}nEXPLODE\texttt{EXPLODE} nn (ttx1x_1 \ldots xmx_m mm), unpacks a Tuple t=(x1,,xm)t=(x_1,\ldots,x_m) and returns its length mm, but only if mn15m\leq n\leq 15. Otherwise throws a type check exception.
  • 6F5k\texttt{6F5}kSETINDEX\texttt{SETINDEX} kk (tt xxtt'), computes Tuple tt' that differs from tt only at position tk+1t'_{k+1}, which is set to xx. In other words, t=t|t'|=|t|, ti=tit'_i=t_i for ik+1i\neq k+1, and tk+1=xt'_{k+1}=x, for given 0k150\leq k\leq15. If ktk\geq|t|, throws a range check exception.
  • 6F50\texttt{6F50}SETFIRST\texttt{SETFIRST} (tt xxtt'), sets the first component of Tuple tt to xx and returns the resulting Tuple tt'.
  • 6F51\texttt{6F51}SETSECOND\texttt{SETSECOND} (tt xxtt'), sets the second component of Tuple tt to xx and returns the resulting Tuple tt'.
  • 6F52\texttt{6F52}SETTHIRD\texttt{SETTHIRD} (tt xxtt'), sets the third component of Tuple tt to xx and returns the resulting Tuple tt'.
  • 6F6k\texttt{6F6}kINDEXQ\texttt{INDEXQ} kk (ttxx), returns the kk-th element of a Tuple tt, where 0n150\leq n\leq 15. In other words, returns xk+1x_{k+1} if t=(x1,,xn)t=(x_1,\ldots,x_n). If knk\geq n, or if tt is Null, returns a Null instead of xx.
  • 6F7k\texttt{6F7}kSETINDEXQ\texttt{SETINDEXQ} kk (tt xxtt'), sets the kk-th component of Tuple tt to xx, where 0k<160\leq k<16, and returns the resulting Tuple tt'. If tk|t|\leq k, first extends the original Tuple to length k+1k+1 by setting all new components to Null. If the original value of tt is Null, treats it as an empty Tuple. If tt is not Null or Tuple, throws an exception. If xx is Null and either tk|t|\leq k or tt is Null, then always returns t=tt'=t (and does not consume tuple creation gas).
  • 6F80\texttt{6F80}TUPLEVAR\texttt{TUPLEVAR} (x1x_1 \ldots xnx_n nntt), creates a new Tuple tt of length nn similarly to TUPLE\texttt{TUPLE}, but with 0n2550\leq n\leq 255 taken from the stack.
  • 6F81\texttt{6F81}INDEXVAR\texttt{INDEXVAR} (tt kkxx), similar to INDEX\texttt{INDEX} kk, but with 0k2540\leq k\leq 254 taken from the stack.
  • 6F82\texttt{6F82}UNTUPLEVAR\texttt{UNTUPLEVAR} (tt nnx1x_1 \ldots xnx_n), similar to UNTUPLE\texttt{UNTUPLE} nn, but with 0n2550\leq n\leq 255 taken from the stack.
  • 6F83\texttt{6F83}UNPACKFIRSTVAR\texttt{UNPACKFIRSTVAR} (tt nnx1x_1 \ldots xnx_n), similar to UNPACKFIRST\texttt{UNPACKFIRST} nn, but with 0n2550\leq n\leq 255 taken from the stack.
  • 6F84\texttt{6F84}EXPLODEVAR\texttt{EXPLODEVAR} (tt nnx1x_1 \ldots xmx_m mm), similar to EXPLODE\texttt{EXPLODE} nn, but with 0n2550\leq n\leq 255 taken from the stack.
  • 6F85\texttt{6F85}SETINDEXVAR\texttt{SETINDEXVAR} (tt xx kktt'), similar to SETINDEX\texttt{SETINDEX} kk, but with 0k2540\leq k\leq 254 taken from the stack.
  • 6F86\texttt{6F86}INDEXVARQ\texttt{INDEXVARQ} (tt kkxx), similar to INDEXQ\texttt{INDEXQ} nn, but with 0k2540\leq k\leq 254 taken from the stack.
  • 6F87\texttt{6F87}SETINDEXVARQ\texttt{SETINDEXVARQ} (tt xx kktt'), similar to SETINDEXQ\texttt{SETINDEXQ} kk, but with 0k2540\leq k\leq 254 taken from the stack.
  • 6F88\texttt{6F88}TLEN\texttt{TLEN} (ttnn), returns the length of a Tuple.
  • 6F89\texttt{6F89}QTLEN\texttt{QTLEN} (ttnn or 1-1), similar to TLEN\texttt{TLEN}, but returns 1-1 if tt is not a Tuple.
  • 6F8A\texttt{6F8A}ISTUPLE\texttt{ISTUPLE} (tt??), returns 1-1 or 00 depending on whether tt is a Tuple.
  • 6F8B\texttt{6F8B}LAST\texttt{LAST} (ttxx), returns the last element ttt_{|t|} of a non-empty Tuple tt.
  • 6F8C\texttt{6F8C}TPUSH\texttt{TPUSH} or COMMA\texttt{COMMA} (tt xxtt'), appends a value xx to a Tuple t=(x1,,xn)t=(x_1,\ldots,x_n), but only if the resulting Tuple t=(x1,,xn,x)t'=(x_1,\ldots,x_n,x) is of length at most 255. Otherwise throws a type check exception.
  • 6F8D\texttt{6F8D}TPOP\texttt{TPOP} (tttt' xx), detaches the last element x=xnx=x_n from a non-empty Tuple t=(x1,,xn)t=(x_1,\ldots,x_n), and returns both the resulting Tuple t=(x1,,xn1)t'=(x_1,\ldots,x_{n-1}) and the original last element xx.
  • 6FA0\texttt{6FA0}NULLSWAPIF\texttt{NULLSWAPIF} (xxxx or \bot xx), pushes a Null under the topmost Integer xx, but only if x0x\neq0.
  • 6FA1\texttt{6FA1}NULLSWAPIFNOT\texttt{NULLSWAPIFNOT} (xxxx or \bot xx), pushes a Null under the topmost Integer xx, but only if x=0x=0. May be used for stack alignment after quiet primitives such as PLDUXQ\texttt{PLDUXQ}.
  • 6FA2\texttt{6FA2}NULLROTRIF\texttt{NULLROTRIF} (xx yyxx yy or \bot xx yy), pushes a Null under the second stack entry from the top, but only if the topmost Integer yy is non-zero.
  • 6FA3\texttt{6FA3}NULLROTRIFNOT\texttt{NULLROTRIFNOT} (xx yyxx yy or \bot xx yy), pushes a Null under the second stack entry from the top, but only if the topmost Integer yy is zero. May be used for stack alignment after quiet primitives such as LDUXQ\texttt{LDUXQ}.
  • 6FA4\texttt{6FA4}NULLSWAPIF2\texttt{NULLSWAPIF2} (xxxx or \bot \bot xx), pushes two Nulls under the topmost Integer xx, but only if x0x\neq0. Equivalent to NULLSWAPIF\texttt{NULLSWAPIF}; NULLSWAPIF\texttt{NULLSWAPIF}.
  • 6FA5\texttt{6FA5}NULLSWAPIFNOT2\texttt{NULLSWAPIFNOT2} (xxxx or \bot \bot xx), pushes two Nulls under the topmost Integer xx, but only if x=0x=0. Equivalent to NULLSWAPIFNOT\texttt{NULLSWAPIFNOT}; NULLSWAPIFNOT\texttt{NULLSWAPIFNOT}.
  • 6FA6\texttt{6FA6}NULLROTRIF2\texttt{NULLROTRIF2} (xx yyxx yy or \bot \bot xx yy), pushes two Nulls under the second stack entry from the top, but only if the topmost Integer yy is non-zero. Equivalent to NULLROTRIF\texttt{NULLROTRIF}; NULLROTRIF\texttt{NULLROTRIF}.
  • 6FA7\texttt{6FA7}NULLROTRIFNOT2\texttt{NULLROTRIFNOT2} (xx yyxx yy or \bot \bot xx yy), pushes two Nulls under the second stack entry from the top, but only if the topmost Integer yy is zero. Equivalent to NULLROTRIFNOT\texttt{NULLROTRIFNOT}; NULLROTRIFNOT\texttt{NULLROTRIFNOT}.
  • 6FBij\texttt{6FB}ijINDEX2\texttt{INDEX2} ii,jj (ttxx), recovers x=(ti+1)j+1x=(t_{i+1})_{j+1} for 0i,j30\leq i,j\leq 3. Equivalent to INDEX\texttt{INDEX} ii; INDEX\texttt{INDEX} jj.
  • 6FB4\texttt{6FB4}CADR\texttt{CADR} (ttxx), recovers x=(t2)1x=(t_2)_1.
  • 6FB5\texttt{6FB5}CDDR\texttt{CDDR} (ttxx), recovers x=(t2)2x=(t_2)_2.
  • 6FE_ijk\texttt{6FE\_}ijkINDEX3\texttt{INDEX3} ii,jj,kk (ttxx), recovers x=((ti+1)j+1)k+1x=\bigl((t_{i+1})_{j+1}\bigr)_{k+1} for 0i,j,k30\leq i,j,k\leq3. Equivalent to INDEX2\texttt{INDEX2} ii,jj; INDEX\texttt{INDEX} kk.
  • 6FD4\texttt{6FD4}CADDR\texttt{CADDR} (ttxx), recovers x=((t2)2)1x=\bigl((t_2)_2\bigr)_1.
  • 6FD5\texttt{6FD5}CDDDR\texttt{CDDDR} (ttxx), recovers x=((t2)2)2x=\bigl((t_2)_2\bigr)_2.

A.4 Constant, or literal primitives

The following primitives push into the stack one literal (or unnamed constant) of some type and range, stored as a part (an immediate argument) of the instruction. Therefore, if the immediate argument is absent or too short, an “invalid or too short opcode” exception (code 6) is thrown.

A.4.1. Integer and boolean constants

  • 7i\texttt{7}iPUSHINT\texttt{PUSHINT} xx with 5x10-5\leq x\leq 10, pushes integer xx into the stack; here ii equals four lower-order bits of xx (i.e., i=xmod16i=x\bmod 16).
  • 70\texttt{70}ZERO\texttt{ZERO}, FALSE\texttt{FALSE}, or PUSHINT 0\texttt{PUSHINT 0}, pushes a zero.
  • 71\texttt{71}ONE\texttt{ONE} or PUSHINT 1\texttt{PUSHINT 1}.
  • 72\texttt{72}TWO\texttt{TWO} or PUSHINT 2\texttt{PUSHINT 2}.
  • 7A\texttt{7A}TEN\texttt{TEN} or PUSHINT\texttt{PUSHINT} 10.
  • 7F\texttt{7F}TRUE\texttt{TRUE} or PUSHINT -1\texttt{PUSHINT -1}.
  • 80xx\texttt{80}xxPUSHINT\texttt{PUSHINT} xxxx with 128xx127-128\leq xx\leq127.
  • 81xxxx\texttt{81}xxxxPUSHINT\texttt{PUSHINT} xxxxxxxx with 215xxxx<215-2^{15}\leq xxxx<2^{15} a signed 16-bit big-endian integer.
  • 81FC18\texttt{81FC18}PUSHINT\texttt{PUSHINT} 1000-1000.
  • 82lxxx\texttt{82}lxxxPUSHINT\texttt{PUSHINT} xxxxxx, where 5-bit 0l300\leq l\leq30 determines the length n=8l+19n=8l+19 of signed big-endian integer xxxxxx. The total length of this instruction is l+4l+4 bytes or n+13=8l+32n+13=8l+32 bits.
  • 821005F5E100\texttt{821005F5E100}PUSHINT\texttt{PUSHINT} 10810^8.
  • 83xx\texttt{83}xxPUSHPOW2\texttt{PUSHPOW2} xx+1xx+1, (quietly) pushes 2xx+12^{xx+1} for 0xx2550\leq xx\leq255.
  • 83FF\texttt{83FF}PUSHNAN\texttt{PUSHNAN}, pushes a NaN\texttt{NaN}.
  • 84xx\texttt{84}xxPUSHPOW2DEC\texttt{PUSHPOW2DEC} xx+1xx+1, pushes 2xx+112^{xx+1}-1 for 0xx2550\leq xx\leq 255.
  • 85xx\texttt{85}xxPUSHNEGPOW2\texttt{PUSHNEGPOW2} xx+1xx+1, pushes 2xx+1-2^{xx+1} for 0xx2550\leq xx\leq 255.
  • 86\texttt{86}, 87\texttt{87} — reserved for integer constants.

A.4.2. Constant slices, continuations, cells, and references

Most of the instructions listed below push literal slices, continuations, cells, and cell references, stored as immediate arguments to the instruction. Therefore, if the immediate argument is absent or too short, an “invalid or too short opcode” exception (code 66) is thrown.
  • 88\texttt{88}PUSHREF\texttt{PUSHREF}, pushes the first reference of cc.code\texttt{cc.code} into the stack as a Cell (and removes this reference from the current continuation).
  • 89\texttt{89}PUSHREFSLICE\texttt{PUSHREFSLICE}, similar to PUSHREF\texttt{PUSHREF}, but converts the cell into a Slice.
  • 8A\texttt{8A}PUSHREFCONT\texttt{PUSHREFCONT}, similar to PUSHREFSLICE\texttt{PUSHREFSLICE}, but makes a simple ordinary Continuation out of the cell.
  • 8Bxsss\texttt{8B}xsssPUSHSLICE sss\texttt{PUSHSLICE sss}, pushes the (prefix) subslice of cc.code\texttt{cc.code} consisting of its first 8x+48x+4 bits and no references (i.e., essentially a bitstring), where 0x150\leq x\leq15. A completion tag is assumed, meaning that all trailing zeroes and the last binary one (if present) are removed from this bitstring. If the original bitstring consists only of zeroes, an empty slice will be pushed.
  • 8B08\texttt{8B08}PUSHSLICE x8_\texttt{PUSHSLICE x8\_}, pushes an empty slice (bitstring ’ ’).
  • 8B04\texttt{8B04}PUSHSLICE x4_\texttt{PUSHSLICE x4\_}, pushes bitstring ’0’\texttt{'0'}.
  • 8B0C\texttt{8B0C}PUSHSLICE xC_\texttt{PUSHSLICE xC\_}, pushes bitstring ’1’\texttt{'1'}.
  • 8Crxxssss\texttt{8C}rxxssssPUSHSLICE ssss\texttt{PUSHSLICE ssss}, pushes the (prefix) subslice of cc.code\texttt{cc.code} consisting of its first 1r+141\leq r+1\leq 4 references and up to first 8xx+18xx+1 bits of data, with 0xx310\leq xx\leq 31. A completion tag is also assumed.
  • 8C01\texttt{8C01} is equivalent to PUSHREFSLICE\texttt{PUSHREFSLICE}.
  • 8Drxxsssss\texttt{8D}rxxsssssPUSHSLICE sssss\texttt{PUSHSLICE sssss}, pushes the subslice of cc.code\texttt{cc.code} consisting of 0r40\leq r\leq 4 references and up to 8xx+68xx+6 bits of data, with 0xx1270\leq xx\leq 127. A completion tag is assumed.
  • 8DE_\texttt{8DE\_} — unused (reserved).
  • 8F_rxxcccc\texttt{8F\_}rxxccccPUSHCONT cccc\texttt{PUSHCONT cccc}, where cccccccc is the simple ordinary continuation made from the first 0r30\leq r\leq 3 references and the first 0xx1270\leq xx\leq 127 bytes of cc.code\texttt{cc.code}.
  • 9xccc\texttt{9}xcccPUSHCONT ccc\texttt{PUSHCONT ccc}, pushes an xx-byte continuation for 0x150\leq x\leq 15.

A.5 Arithmetic primitives

A.5.1. Addition, subtraction, multiplication

  • A0\texttt{A0}ADD\texttt{ADD} (xx yyx+yx+y), adds together two integers.
  • A1\texttt{A1}SUB\texttt{SUB} (xx yyxyx-y).
  • A2\texttt{A2}SUBR\texttt{SUBR} (xx yyyxy-x), equivalent to SWAP\texttt{SWAP}; SUB\texttt{SUB}.
  • A3\texttt{A3}NEGATE\texttt{NEGATE} (xxx-x), equivalent to MULCONST\texttt{MULCONST} 1-1 or to ZERO\texttt{ZERO}; SUBR\texttt{SUBR}. Notice that it triggers an integer overflow exception if x=2256x=-2^{256}.
  • A4\texttt{A4}INC\texttt{INC} (xxx+1x+1), equivalent to ADDCONST 1\texttt{ADDCONST 1}.
  • A5\texttt{A5}DEC\texttt{DEC} (xxx1x-1), equivalent to ADDCONST\texttt{ADDCONST} 1-1.
  • A6cc\texttt{A6}ccADDCONST\texttt{ADDCONST} cccc (xxx+ccx+cc), 128cc127-128\leq cc\leq127.
  • A7cc\texttt{A7}ccMULCONST\texttt{MULCONST} cccc (xxxccx\cdot cc), 128cc127-128\leq cc\leq127.
  • A8\texttt{A8}MUL\texttt{MUL} (xx yyxyxy).

A.5.2. Division

The general encoding of a DIV\texttt{DIV}, DIVMOD\texttt{DIVMOD}, or MOD\texttt{MOD} operation is A9mscdf\texttt{A9}mscdf, with an optional pre-multiplication and an optional replacement of the division or multiplication by a shift. Variable one- or two-bit fields mm, ss, cc, dd, and ff are as follows:
  • 0m10\leq m\leq1 — Indicates whether there is pre-multiplication (MULDIV\texttt{MULDIV} operation and its variants), possibly replaced by a left shift.
  • 0s20\leq s\leq2 — Indicates whether either the multiplication or the division have been replaced by shifts: s=0s=0—no replacement, s=1s=1—division replaced by a right shift, s=2s=2—multiplication replaced by a left shift (possible only for m=1m=1).
  • 0c10\leq c\leq1 — Indicates whether there is a constant one-byte argument tttt for the shift operator (if s0s\neq0). For s=0s=0, c=0c=0. If c=1c=1, then 0tt2550\leq tt\leq 255, and the shift is performed by tt+1tt+1 bits. If s0s\neq0 and c=0c=0, then the shift amount is provided to the instruction as a top-of-stack Integer in range 02560\ldots256.
  • 1d31\leq d\leq3 — Indicates which results of division are required: 11—only the quotient, 22—only the remainder, 33—both.
  • 0f20\leq f\leq2 — Rounding mode: 00—floor, 11—nearest integer, 22—ceiling (cf. 1.5.6).
Examples:
  • A904\texttt{A904}DIV\texttt{DIV} (xx yyq:=x/yq:=\lfloor x/y\rfloor).
  • A905\texttt{A905}DIVR\texttt{DIVR} (xx yyq:=x/y+1/2q':=\lfloor x/y+1/2\rfloor).
  • A906\texttt{A906}DIVC\texttt{DIVC} (xx yyq:=x/yq'':=\lceil x/y\rceil).
  • A908\texttt{A908}MOD\texttt{MOD} (xx yyrr), where q:=x/yq:=\lfloor x/y\rfloor, r:=xmody:=xyqr:=x\bmod y:=x-yq.
  • A90C\texttt{A90C}DIVMOD\texttt{DIVMOD} (xx yyqq rr), where q:=x/yq:=\lfloor x/y\rfloor, r:=xyqr:=x-yq.
  • A90D\texttt{A90D}DIVMODR\texttt{DIVMODR} (xx yyqq' rr'), where q:=x/y+1/2q':=\lfloor x/y+1/2\rfloor, r:=xyqr':=x-yq'.
  • A90E\texttt{A90E}DIVMODC\texttt{DIVMODC} (xx yyqq'' rr''), where q:=x/yq'':=\lceil x/y\rceil, r:=xyqr'':=x-yq''.
  • A924\texttt{A924} — same as RSHIFT\texttt{RSHIFT}: (xx yyx2y\lfloor x\cdot 2^{-y}\rfloor) for 0y2560\leq y\leq 256.
  • A934tt\texttt{A934}tt — same as RSHIFT\texttt{RSHIFT} tt+1tt+1: (xxx2tt1\lfloor x\cdot 2^{-tt-1}\rfloor).
  • A938tt\texttt{A938}ttMODPOW2\texttt{MODPOW2} tt+1tt+1: (xxxmod2tt+1x\bmod 2^{tt+1}).
  • A985\texttt{A985}MULDIVR\texttt{MULDIVR} (xx yy zzqq'), where q=xy/z+1/2q'=\lfloor xy/z+1/2\rfloor.
  • A988\texttt{A988}MULMOD\texttt{MULMOD} (xx yy zzrr), where r=xymodz=xyqzr=xy\bmod z=xy-qz, q=xy/zq=\lfloor xy/z\rfloor. This operation always succeeds for z0z\neq0 and returns the correct value of rr, even if the intermediate result xyxy or the quotient qq do not fit into 257 bits.
  • A98C\texttt{A98C}MULDIVMOD\texttt{MULDIVMOD} (xx yy zzqq rr), where q:=xy/zq:=\lfloor x\cdot y/z\rfloor, r:=xymodzr:=x\cdot y\bmod z (same as */MOD\texttt{*/MOD} in Forth).
  • A9A4\texttt{A9A4}MULRSHIFT\texttt{MULRSHIFT} (xx yy zzxy2z\lfloor xy\cdot2^{-z}\rfloor) for 0z2560\leq z\leq 256.
  • A9A5\texttt{A9A5}MULRSHIFTR\texttt{MULRSHIFTR} (xx yy zzxy2z+1/2\lfloor xy\cdot2^{-z}+1/2\rfloor) for 0z2560\leq z\leq 256.
  • A9B4tt\texttt{A9B4}ttMULRSHIFT\texttt{MULRSHIFT} tt+1tt+1 (xx yyxy2tt1\lfloor xy\cdot 2^{-tt-1}\rfloor).
  • A9B5tt\texttt{A9B5}ttMULRSHIFTR\texttt{MULRSHIFTR} tt+1tt+1 (xx yyxy2tt1+1/2\lfloor xy\cdot 2^{-tt-1}+1/2\rfloor).
  • A9C4\texttt{A9C4}LSHIFTDIV\texttt{LSHIFTDIV} (xx yy zz2zx/y\lfloor 2^zx/y\rfloor) for 0z2560\leq z\leq 256.
  • A9C5\texttt{A9C5}LSHIFTDIVR\texttt{LSHIFTDIVR} (xx yy zz2zx/y+1/2\lfloor 2^zx/y+1/2\rfloor) for 0z2560\leq z\leq 256.
  • A9D4tt\texttt{A9D4}ttLSHIFTDIV\texttt{LSHIFTDIV} tt+1tt+1 (xx yy2tt+1x/y\lfloor 2^{tt+1}x/y\rfloor).
  • A9D5tt\texttt{A9D5}ttLSHIFTDIVR\texttt{LSHIFTDIVR} tt+1tt+1 (xx yy2tt+1x/y+1/2\lfloor 2^{tt+1}x/y+1/2\rfloor).
The most useful of these operations are DIV\texttt{DIV}, DIVMOD\texttt{DIVMOD}, MOD\texttt{MOD}, DIVR\texttt{DIVR}, DIVC\texttt{DIVC}, MODPOW2\texttt{MODPOW2} tt, and RSHIFTR\texttt{RSHIFTR} tt (for integer arithmetic); and MULDIVMOD\texttt{MULDIVMOD}, MULDIV\texttt{MULDIV}, MULDIVR\texttt{MULDIVR}, LSHIFTDIVR\texttt{LSHIFTDIVR} tt, and MULRSHIFTR\texttt{MULRSHIFTR} tt (for fixed-point arithmetic).

A.5.3. Shifts, logical operations

  • AAcc\texttt{AA}ccLSHIFT\texttt{LSHIFT} cc+1cc+1 (xxx2cc+1x\cdot2^{cc+1}), 0cc2550\leq cc\leq255.
  • AA00\texttt{AA00}LSHIFT 1\texttt{LSHIFT 1}, equivalent to MULCONST 2\texttt{MULCONST 2} or to Forth’s 2*\texttt{2*}.
  • ABcc\texttt{AB}ccRSHIFT\texttt{RSHIFT} cc+1cc+1 (xxx2cc1\lfloor x\cdot2^{-cc-1}\rfloor), 0cc2550\leq cc\leq255.
  • AC\texttt{AC}LSHIFT\texttt{LSHIFT} (xx yyx2yx\cdot 2^y), 0y10230\leq y\leq 1023.
  • AD\texttt{AD}RSHIFT\texttt{RSHIFT} (xx yyx2y\lfloor x\cdot 2^{-y}\rfloor), 0y10230\leq y\leq 1023.
  • AE\texttt{AE}POW2\texttt{POW2} (yy2y2^y), 0y10230\leq y\leq1023, equivalent to ONE\texttt{ONE}; SWAP\texttt{SWAP}; LSHIFT\texttt{LSHIFT}.
  • AF\texttt{AF} — reserved.
  • B0\texttt{B0}AND\texttt{AND} (xx yyx&yx\&y), bitwise “and” of two signed integers xx and yy, sign-extended to infinity.
  • B1\texttt{B1}OR\texttt{OR} (xx yyxyx\vee y), bitwise “or” of two integers.
  • B2\texttt{B2}XOR\texttt{XOR} (xx yyxyx\oplus y), bitwise “xor” of two integers.
  • B3\texttt{B3}NOT\texttt{NOT} (xxx1=1xx\oplus-1=-1-x), bitwise “not” of an integer.
  • B4cc\texttt{B4}ccFITS\texttt{FITS} cc+1cc+1 (xxxx), checks whether xx is a cc+1cc+1-bit signed integer for 0cc2550\leq cc\leq 255 (i.e., whether 2ccx<2cc-2^{cc}\leq x<2^{cc}). If not, either triggers an integer overflow exception, or replaces xx with a NaN\texttt{NaN} (quiet version).
  • B400\texttt{B400}FITS 1\texttt{FITS 1} or CHKBOOL\texttt{CHKBOOL} (xxxx), checks whether xx is a “boolean value” (i.e., either 0 or -1).
  • B5cc\texttt{B5}ccUFITS\texttt{UFITS} cc+1cc+1 (xxxx), checks whether xx is a cc+1cc+1-bit unsigned integer for 0cc2550\leq cc\leq 255 (i.e., whether 0x<2cc+10\leq x<2^{cc+1}).
  • B500\texttt{B500}UFITS 1\texttt{UFITS 1} or CHKBIT\texttt{CHKBIT}, checks whether xx is a binary digit (i.e., zero or one).
  • B600\texttt{B600}FITSX\texttt{FITSX} (xx ccxx), checks whether xx is a cc-bit signed integer for 0c10230\leq c\leq 1023.
  • B601\texttt{B601}UFITSX\texttt{UFITSX} (xx ccxx), checks whether xx is a cc-bit unsigned integer for 0c10230\leq c\leq 1023.
  • B602\texttt{B602}BITSIZE\texttt{BITSIZE} (xxcc), computes smallest c0c\geq0 such that xx fits into a cc-bit signed integer (2c1c<2c1-2^{c-1}\leq c<2^{c-1}).
  • B603\texttt{B603}UBITSIZE\texttt{UBITSIZE} (xxcc), computes smallest c0c\geq0 such that xx fits into a cc-bit unsigned integer (0x<2c0\leq x<2^c), or throws a range check exception.
  • B608\texttt{B608}MIN\texttt{MIN} (xx yyxx or yy), computes the minimum of two integers xx and yy.
  • B609\texttt{B609}MAX\texttt{MAX} (xx yyxx or yy), computes the maximum of two integers xx and yy.
  • B60A\texttt{B60A}MINMAX\texttt{MINMAX} or INTSORT2\texttt{INTSORT2} (xx yyxx yy or yy xx), sorts two integers. Quiet version of this operation returns two NaN\texttt{NaN}s if any of the arguments are NaN\texttt{NaN}s.
  • B60B\texttt{B60B}ABS\texttt{ABS} (xxx|x|), computes the absolute value of an integer xx.

A.5.4. Quiet arithmetic primitives

We opted to make all arithmetic operations “non-quiet” (signaling) by default, and create their quiet counterparts by means of a prefix. Such an encoding is definitely sub-optimal. It is not yet clear whether it should be done in this way, or in the opposite way by making all arithmetic operations quiet by default, or whether quiet and non-quiet operations should be given opcodes of equal length; this can only be settled by practice.
  • B7xx\texttt{B7}xxQUIET\texttt{QUIET} prefix, transforming any arithmetic operation into its “quiet” variant, indicated by prefixing a Q to its mnemonic. Such operations return NaN\texttt{NaN}s instead of throwing integer overflow exceptions if the results do not fit in Integers, or if one of their arguments is a NaN\texttt{NaN}. Notice that this does not extend to shift amounts and other parameters that must be within a small range (e.g., 0—1023). Also notice that this does not disable type-checking exceptions if a value of a type other than Integer is supplied.
  • B7A0\texttt{B7A0}QADD\texttt{QADD} (xx yyx+yx+y), always works if xx and yy are Integers, but returns a NaN\texttt{NaN} if the addition cannot be performed.
  • B7A8\texttt{B7A8}QMUL\texttt{QMUL} (xx yyxyxy), returns the product of xx and yy if 2256xy<2256-2^{256}\leq xy<2^{256}. Otherwise returns a NaN\texttt{NaN}, even if x=0x=0 and yy is a NaN\texttt{NaN}.
  • B7A904\texttt{B7A904}QDIV\texttt{QDIV} (xx yyx/y\lfloor x/y\rfloor), returns a NaN\texttt{NaN} if y=0y=0, or if y=1y=-1 and x=2256x=-2^{256}, or if either of xx or yy is a NaN\texttt{NaN}.
  • B7A98C\texttt{B7A98C}QMULDIVMOD\texttt{QMULDIVMOD} (xx yy zzqq rr), where q:=xy/zq:=\lfloor x\cdot y/z\rfloor, r:=xymodzr:=x\cdot y\bmod z. If z=0z=0, or if at least one of xx, yy, or zz is a NaN\texttt{NaN}, both qq and rr are set to NaN\texttt{NaN}. Otherwise the correct value of rr is always returned, but qq is replaced with NaN\texttt{NaN} if q<2256q<-2^{256} or q2256q\geq2^{256}.
  • B7B0\texttt{B7B0}QAND\texttt{QAND} (xx yyx&yx\&y), bitwise “and” (similar to AND\texttt{AND}), but returns a NaN\texttt{NaN} if either xx or yy is a NaN\texttt{NaN} instead of throwing an integer overflow exception. However, if one of the arguments is zero, and the other is a NaN\texttt{NaN}, the result is zero.
  • B7B1\texttt{B7B1}QOR\texttt{QOR} (xx yyxyx\vee y), bitwise “or”. If x=1x=-1 or y=1y=-1, the result is always 1-1, even if the other argument is a NaN\texttt{NaN}.
  • B7B507\texttt{B7B507}QUFITS 8\texttt{QUFITS 8} (xxxx'), checks whether xx is an unsigned byte (i.e., whether 0x<280\leq x<2^8), and replaces xx with a NaN\texttt{NaN} if this is not the case; leaves xx intact otherwise (i.e., if xx is an unsigned byte or a NaN\texttt{NaN}).

A.6 Comparison primitives

A.6.1. Integer comparison

All integer comparison primitives return integer 1-1 (“true”) or 00 (“false”) to indicate the result of the comparison. We do not define their “boolean circuit” counterparts, which would transfer control to c0\texttt{c0} or c1\texttt{c1} depending on the result of the comparison. If needed, such instructions can be simulated with the aid of RETBOOL\texttt{RETBOOL}. Quiet versions of integer comparison primitives are also available, encoded with the aid of the QUIET\texttt{QUIET} prefix (B7\texttt{B7}). If any of the integers being compared are NaN\texttt{NaN}s, the result of a quiet comparison will also be a NaN\texttt{NaN} (“undefined”), instead of a 1-1 (“yes”) or 00 (“no”), thus effectively supporting ternary logic.
  • B8\texttt{B8}SGN\texttt{SGN} (xxsgn(x)\text{sgn}(x)), computes the sign of an integer xx: 1-1 if x<0x<0, 00 if x=0x=0, 11 if x>0x>0.
  • B9\texttt{B9}LESS\texttt{LESS} (xx yyx<yx<y), returns 1-1 if x<yx<y, 00 otherwise.
  • BA\texttt{BA}EQUAL\texttt{EQUAL} (xx yyx=yx=y), returns 1-1 if x=yx=y, 00 otherwise.
  • BB\texttt{BB}LEQ\texttt{LEQ} (xx yyxyx\leq y).
  • BC\texttt{BC}GREATER\texttt{GREATER} (xx yyx>yx>y).
  • BD\texttt{BD}NEQ\texttt{NEQ} (xx yyxyx\neq y), equivalent to EQUAL\texttt{EQUAL}; NOT\texttt{NOT}.
  • BE\texttt{BE}GEQ\texttt{GEQ} (xx yyxyx\geq y), equivalent to LESS\texttt{LESS}; NOT\texttt{NOT}.
  • BF\texttt{BF}CMP\texttt{CMP} (xx yysgn(xy)\text{sgn}(x-y)), computes the sign of xyx-y: 1-1 if x<yx<y, 00 if x=yx=y, 11 if x>yx>y. No integer overflow can occur here unless xx or yy is a NaN\texttt{NaN}.
  • C0yy\texttt{C0}yyEQINT\texttt{EQINT} yyyy (xxx=yyx=yy) for 27yy<27-2^7\leq yy<2^7.
  • C000\texttt{C000}ISZERO\texttt{ISZERO}, checks whether an integer is zero. Corresponds to Forth’s 0=\texttt{0=}.
  • C1yy\texttt{C1}yyLESSINT\texttt{LESSINT} yyyy (xxx<yyx<yy) for 27yy<27-2^7\leq yy<2^7.
  • C100\texttt{C100}ISNEG\texttt{ISNEG}, checks whether an integer is negative. Corresponds to Forth’s 0<\texttt{0<}.
  • C101\texttt{C101}ISNPOS\texttt{ISNPOS}, checks whether an integer is non-positive.
  • C2yy\texttt{C2}yyGTINT\texttt{GTINT} yyyy (xxx>yyx>yy) for 27yy<27-2^7\leq yy<2^7.
  • C200\texttt{C200}ISPOS\texttt{ISPOS}, checks whether an integer is positive. Corresponds to Forth’s 0>\texttt{0>}.
  • C2FF\texttt{C2FF}ISNNEG\texttt{ISNNEG}, checks whether an integer is non-negative.
  • C3yy\texttt{C3}yyNEQINT\texttt{NEQINT} yyyy (xxxyyx\neq yy) for 27yy<27-2^7\leq yy<2^7.
  • C4\texttt{C4}ISNAN\texttt{ISNAN} (xxx=NaNx=\texttt{NaN}), checks whether xx is a NaN\texttt{NaN}.
  • C5\texttt{C5}CHKNAN\texttt{CHKNAN} (xxxx), throws an arithmetic overflow exception if xx is a NaN\texttt{NaN}.
  • C6\texttt{C6} — reserved for integer comparison.

A.6.2. Other comparison

Most of these “other comparison” primitives actually compare the data portions of Slices as bitstrings.
  • C700\texttt{C700}SEMPTY\texttt{SEMPTY} (sss=s=\emptyset), checks whether a Slice ss is empty (i.e., contains no bits of data and no cell references).
  • C701\texttt{C701}SDEMPTY\texttt{SDEMPTY} (ssss\approx\emptyset), checks whether Slice ss has no bits of data.
  • C702\texttt{C702}SREMPTY\texttt{SREMPTY} (ssr(s)=0r(s)=0), checks whether Slice ss has no references.
  • C703\texttt{C703}SDFIRST\texttt{SDFIRST} (sss0=1s_0=1), checks whether the first bit of Slice ss is a one.
  • C704\texttt{C704}SDLEXCMP\texttt{SDLEXCMP} (ss ss'cc), compares the data of ss lexicographically with the data of ss', returning 1-1, 0, or 1 depending on the result.
  • C705\texttt{C705}SDEQ\texttt{SDEQ} (ss ss'sss\approx s'), checks whether the data parts of ss and ss' coincide, equivalent to SDLEXCMP\texttt{SDLEXCMP}; ISZERO\texttt{ISZERO}.
  • C708\texttt{C708}SDPFX\texttt{SDPFX} (ss ss'??), checks whether ss is a prefix of ss'.
  • C709\texttt{C709}SDPFXREV\texttt{SDPFXREV} (ss ss'??), checks whether ss' is a prefix of ss, equivalent to SWAP\texttt{SWAP}; SDPFX\texttt{SDPFX}.
  • C70A\texttt{C70A}SDPPFX\texttt{SDPPFX} (ss ss'??), checks whether ss is a proper prefix of ss' (i.e., a prefix distinct from ss').
  • C70B\texttt{C70B}SDPPFXREV\texttt{SDPPFXREV} (ss ss'??), checks whether ss' is a proper prefix of ss.
  • C70C\texttt{C70C}SDSFX\texttt{SDSFX} (ss ss'??), checks whether ss is a suffix of ss'.
  • C70D\texttt{C70D}SDSFXREV\texttt{SDSFXREV} (ss ss'??), checks whether ss' is a suffix of ss.
  • C70E\texttt{C70E}SDPSFX\texttt{SDPSFX} (ss ss'??), checks whether ss is a proper suffix of ss'.
  • C70F\texttt{C70F}SDPSFXREV\texttt{SDPSFXREV} (ss ss'??), checks whether ss' is a proper suffix of ss.
  • C710\texttt{C710}SDCNTLEAD0\texttt{SDCNTLEAD0} (ssnn), returns the number of leading zeroes in ss.
  • C711\texttt{C711}SDCNTLEAD1\texttt{SDCNTLEAD1} (ssnn), returns the number of leading ones in ss.
  • C712\texttt{C712}SDCNTTRAIL0\texttt{SDCNTTRAIL0} (ssnn), returns the number of trailing zeroes in ss.
  • C713\texttt{C713}SDCNTTRAIL1\texttt{SDCNTTRAIL1} (ssnn), returns the number of trailing ones in ss.

A.7 Cell primitives

The cell primitives are mostly either cell serialization primitives, which work with Builders, or cell deserialization primitives, which work with Slices.

A.7.1. Cell serialization primitives

All these primitives first check whether there is enough space in the Builder, and only then check the range of the value being serialized.
  • C8\texttt{C8}NEWC\texttt{NEWC} ( — bb), creates a new empty Builder.
  • C9\texttt{C9}ENDC\texttt{ENDC} (bbcc), converts a Builder into an ordinary Cell.
  • CAcc\texttt{CA}ccSTI\texttt{STI} cc+1cc+1 (xx bbbb'), stores a signed cc+1cc+1-bit integer xx into Builder bb for 0cc2550\leq cc\leq 255, throws a range check exception if xx does not fit into cc+1cc+1 bits.
  • CBcc\texttt{CB}ccSTU\texttt{STU} cc+1cc+1 (xx bbbb'), stores an unsigned cc+1cc+1-bit integer xx into Builder bb. In all other respects it is similar to STI\texttt{STI}.
  • CC\texttt{CC}STREF\texttt{STREF} (cc bbbb'), stores a reference to Cell cc into Builder bb.
  • CD\texttt{CD}STBREFR\texttt{STBREFR} or ENDCST\texttt{ENDCST} (bb bb''bb), equivalent to ENDC\texttt{ENDC}; SWAP\texttt{SWAP}; STREF\texttt{STREF}.
  • CE\texttt{CE}STSLICE\texttt{STSLICE} (ss bbbb'), stores Slice ss into Builder bb.
  • CF00\texttt{CF00}STIX\texttt{STIX} (xx bb llbb'), stores a signed ll-bit integer xx into bb for 0l2570\leq l\leq 257.
  • CF01\texttt{CF01}STUX\texttt{STUX} (xx bb llbb'), stores an unsigned ll-bit integer xx into bb for 0l2560\leq l\leq 256.
  • CF02\texttt{CF02}STIXR\texttt{STIXR} (bb xx llbb'), similar to STIX\texttt{STIX}, but with arguments in a different order.
  • CF03\texttt{CF03}STUXR\texttt{STUXR} (bb xx llbb'), similar to STUX\texttt{STUX}, but with arguments in a different order.
  • CF04\texttt{CF04}STIXQ\texttt{STIXQ} (xx bb llxx bb ff or bb' 00), a quiet version of STIX\texttt{STIX}. If there is no space in bb, sets b=bb'=b and f=1f=-1. If xx does not fit into ll bits, sets b=bb'=b and f=1f=1. If the operation succeeds, bb' is the new Builder and f=0f=0. However, 0l2570\leq l\leq 257, with a range check exception if this is not so.
  • CF05\texttt{CF05}STUXQ\texttt{STUXQ} (xx bb llbb' ff).
  • CF06\texttt{CF06}STIXRQ\texttt{STIXRQ} (bb xx llbb xx ff or bb' 00).
  • CF07\texttt{CF07}STUXRQ\texttt{STUXRQ} (bb xx llbb xx ff or bb' 00).
  • CF08cc\texttt{CF08}cc — a longer version of STI\texttt{STI} cc+1cc+1.
  • CF09cc\texttt{CF09}cc — a longer version of STU\texttt{STU} cc+1cc+1.
  • CF0Acc\texttt{CF0A}ccSTIR\texttt{STIR} cc+1cc+1 (bb xxbb'), equivalent to SWAP\texttt{SWAP}; STI\texttt{STI} cc+1cc+1.
  • CF0Bcc\texttt{CF0B}ccSTUR\texttt{STUR} cc+1cc+1 (bb xxbb'), equivalent to SWAP\texttt{SWAP}; STU\texttt{STU} cc+1cc+1.
  • CF0Ccc\texttt{CF0C}ccSTIQ\texttt{STIQ} cc+1cc+1 (xx bbxx bb ff or bb' 00).
  • CF0Dcc\texttt{CF0D}ccSTUQ\texttt{STUQ} cc+1cc+1 (xx bbxx bb ff or bb' 00).
  • CF0Ecc\texttt{CF0E}ccSTIRQ\texttt{STIRQ} cc+1cc+1 (bb xxbb xx ff or bb' 00).
  • CF0Fcc\texttt{CF0F}ccSTURQ\texttt{STURQ} cc+1cc+1 (bb xxbb xx ff or bb' 00).
  • CF10\texttt{CF10} — a longer version of STREF\texttt{STREF} (cc bbbb').
  • CF11\texttt{CF11}STBREF\texttt{STBREF} (bb' bbbb''), equivalent to SWAP\texttt{SWAP}; STBREFREV\texttt{STBREFREV}.
  • CF12\texttt{CF12} — a longer version of STSLICE\texttt{STSLICE} (ss bbbb').
  • CF13\texttt{CF13}STB\texttt{STB} (bb' bbbb''), appends all data from Builder bb' to Builder bb.
  • CF14\texttt{CF14}STREFR\texttt{STREFR} (bb ccbb').
  • CF15\texttt{CF15}STBREFR\texttt{STBREFR} (bb bb'bb''), a longer encoding of STBREFR\texttt{STBREFR}.
  • CF16\texttt{CF16}STSLICER\texttt{STSLICER} (bb ssbb').
  • CF17\texttt{CF17}STBR\texttt{STBR} (bb bb'bb''), concatenates two Builders, equivalent to SWAP\texttt{SWAP}; STB\texttt{STB}.
  • CF18\texttt{CF18}STREFQ\texttt{STREFQ} (cc bbcc bb 1-1 or bb' 00).
  • CF19\texttt{CF19}STBREFQ\texttt{STBREFQ} (bb' bbbb' bb 1-1 or bb'' 00).
  • CF1A\texttt{CF1A}STSLICEQ\texttt{STSLICEQ} (ss bbss bb 1-1 or bb' 00).
  • CF1B\texttt{CF1B}STBQ\texttt{STBQ} (bb' bbbb' bb 1-1 or bb'' 00).
  • CF1C\texttt{CF1C}STREFRQ\texttt{STREFRQ} (bb ccbb cc 1-1 or bb' 00).
  • CF1D\texttt{CF1D}STBREFRQ\texttt{STBREFRQ} (bb bb'bb bb' 1-1 or bb'' 00).
  • CF1E\texttt{CF1E}STSLICERQ\texttt{STSLICERQ} (bb ssbb ss 1-1 or bb'' 00).
  • CF1F\texttt{CF1F}STBRQ\texttt{STBRQ} (bb bb'bb bb' 1-1 or bb'' 00).
  • CF20\texttt{CF20}STREFCONST\texttt{STREFCONST}, equivalent to PUSHREF\texttt{PUSHREF}; STREFR\texttt{STREFR}.
  • CF21\texttt{CF21}STREF2CONST\texttt{STREF2CONST}, equivalent to STREFCONST\texttt{STREFCONST}; STREFCONST\texttt{STREFCONST}.
  • CF23\texttt{CF23}ENDXC\texttt{ENDXC} (bb xxcc), if x0x\neq0, creates a special or exotic cell (cf. 3.1.2) from Builder bb. The type of the exotic cell must be stored in the first 8 bits of bb. If x=0x=0, it is equivalent to ENDC\texttt{ENDC}. Otherwise some validity checks on the data and references of bb are performed before creating the exotic cell.
  • CF28\texttt{CF28}STILE4\texttt{STILE4} (xx bbbb'), stores a little-endian signed 32-bit integer.
  • CF29\texttt{CF29}STULE4\texttt{STULE4} (xx bbbb'), stores a little-endian unsigned 32-bit integer.
  • CF2A\texttt{CF2A}STILE8\texttt{STILE8} (xx bbbb'), stores a little-endian signed 64-bit integer.
  • CF2B\texttt{CF2B}STULE8\texttt{STULE8} (xx bbbb'), stores a little-endian unsigned 64-bit integer.
  • CF30\texttt{CF30}BDEPTH\texttt{BDEPTH} (bbxx), returns the depth of Builder bb. If no cell references are stored in bb, then x=0x=0; otherwise xx is one plus the maximum of depths of cells referred to from bb.
  • CF31\texttt{CF31}BBITS\texttt{BBITS} (bbxx), returns the number of data bits already stored in Builder bb.
  • CF32\texttt{CF32}BREFS\texttt{BREFS} (bbyy), returns the number of cell references already stored in bb.
  • CF33\texttt{CF33}BBITREFS\texttt{BBITREFS} (bbxx yy), returns the numbers of both data bits and cell references in bb.
  • CF35\texttt{CF35}BREMBITS\texttt{BREMBITS} (bbxx'), returns the number of data bits that can still be stored in bb.
  • CF36\texttt{CF36}BREMREFS\texttt{BREMREFS} (bbyy').
  • CF37\texttt{CF37}BREMBITREFS\texttt{BREMBITREFS} (bbxx' yy').
  • CF38cc\texttt{CF38}ccBCHKBITS\texttt{BCHKBITS} cc+1cc+1 (bb —), checks whether cc+1cc+1 bits can be stored into bb, where 0cc2550\leq cc\leq 255.
  • CF39\texttt{CF39}BCHKBITS\texttt{BCHKBITS} (bb xx — ), checks whether xx bits can be stored into bb, 0x10230\leq x\leq 1023. If there is no space for xx more bits in bb, or if xx is not within the range 010230\ldots1023, throws an exception.
  • CF3A\texttt{CF3A}BCHKREFS\texttt{BCHKREFS} (bb yy — ), checks whether yy references can be stored into bb, 0y70\leq y\leq 7.
  • CF3B\texttt{CF3B}BCHKBITREFS\texttt{BCHKBITREFS} (bb xx yy — ), checks whether xx bits and yy references can be stored into bb, 0x10230\leq x\leq 1023, 0y70\leq y\leq 7.
  • CF3Ccc\texttt{CF3C}ccBCHKBITSQ\texttt{BCHKBITSQ} cc+1cc+1 (bb??), checks whether cc+1cc+1 bits can be stored into bb, where 0cc2550\leq cc\leq 255.
  • CF3D\texttt{CF3D}BCHKBITSQ\texttt{BCHKBITSQ} (bb xx??), checks whether xx bits can be stored into bb, 0x10230\leq x\leq 1023.
  • CF3E\texttt{CF3E}BCHKREFSQ\texttt{BCHKREFSQ} (bb yy??), checks whether yy references can be stored into bb, 0y70\leq y\leq 7.
  • CF3F\texttt{CF3F}BCHKBITREFSQ\texttt{BCHKBITREFSQ} (bb xx yy??), checks whether xx bits and yy references can be stored into bb, 0x10230\leq x\leq 1023, 0y70\leq y\leq 7.
  • CF40\texttt{CF40}STZEROES\texttt{STZEROES} (bb nnbb'), stores nn binary zeroes into Builder bb.
  • CF41\texttt{CF41}STONES\texttt{STONES} (bb nnbb'), stores nn binary ones into Builder bb.
  • CF42\texttt{CF42}STSAME\texttt{STSAME} (bb nn xxbb'), stores nn binary xxes (0x10\leq x\leq1) into Builder bb.
  • CFC0_xysss\texttt{CFC0\_xysss}STSLICECONST\texttt{STSLICECONST} ssssss (bbbb'), stores a constant subslice ssssss consisting of 0x30\leq x\leq 3 references and up to 8y+18y+1 data bits, with 0y70\leq y\leq 7. Completion bit is assumed.
  • CF81\texttt{CF81}STSLICECONST ’0’\texttt{STSLICECONST '0'} or STZERO\texttt{STZERO} (bbbb'), stores one binary zero.
  • CF83\texttt{CF83}STSLICECONST ’1’\texttt{STSLICECONST '1'} or STONE\texttt{STONE} (bbbb'), stores one binary one.
  • CFA2\texttt{CFA2} — equivalent to STREFCONST\texttt{STREFCONST}.
  • CFA3\texttt{CFA3} — almost equivalent to STSLICECONST ’1’\texttt{STSLICECONST '1'}; STREFCONST\texttt{STREFCONST}.
  • CFC2\texttt{CFC2} — equivalent to STREF2CONST\texttt{STREF2CONST}.
  • CFE2\texttt{CFE2}STREF3CONST\texttt{STREF3CONST}.

A.7.2. Cell deserialization primitives

  • D0\texttt{D0}CTOS\texttt{CTOS} (ccss), converts a Cell into a Slice. Notice that cc must be either an ordinary cell, or an exotic cell (cf. 3.1.2) which is automatically loaded to yield an ordinary cell cc', converted into a Slice afterwards.
  • D1\texttt{D1}ENDS\texttt{ENDS} (ss — ), removes a Slice ss from the stack, and throws an exception if it is not empty.
  • D2\texttt{D2} ccccLDI\texttt{LDI} cc+1cc+1 (ssxx ss'), loads (i.e., parses) a signed cc+1cc+1-bit integer xx from Slice ss, and returns the remainder of ss as ss'.
  • D3\texttt{D3} ccccLDU\texttt{LDU} cc+1cc+1 (ssxx ss'), loads an unsigned cc+1cc+1-bit integer xx from Slice ss.
  • D4\texttt{D4}LDREF\texttt{LDREF} (sscc ss'), loads a cell reference cc from ss.
  • D5\texttt{D5}LDREFRTOS\texttt{LDREFRTOS} (ssss' ss''), equivalent to LDREF\texttt{LDREF}; SWAP\texttt{SWAP}; CTOS\texttt{CTOS}.
  • D6\texttt{D6} ccccLDSLICE\texttt{LDSLICE} cc+1cc+1 (ssss'' ss'), cuts the next cc+1cc+1 bits of ss into a separate Slice ss''.
  • D700\texttt{D700}LDIX\texttt{LDIX} (ss llxx ss'), loads a signed ll-bit (0l2570\leq l\leq 257) integer xx from Slice ss, and returns the remainder of ss as ss'.
  • D701\texttt{D701}LDUX\texttt{LDUX} (ss llxx ss'), loads an unsigned ll-bit integer xx from (the first ll bits of) ss, with 0l2560\leq l\leq 256.
  • D702\texttt{D702}PLDIX\texttt{PLDIX} (ss llxx), preloads a signed ll-bit integer from Slice ss, for 0l2570\leq l\leq 257.
  • D703\texttt{D703}PLDUX\texttt{PLDUX} (ss llxx), preloads an unsigned ll-bit integer from ss, for 0l2560\leq l\leq 256.
  • D704\texttt{D704}LDIXQ\texttt{LDIXQ} (ss llxx ss' 1-1 or ss 00), quiet version of LDIX\texttt{LDIX}: loads a signed ll-bit integer from ss similarly to LDIX\texttt{LDIX}, but returns a success flag, equal to 1-1 on success or to 00 on failure (if ss does not have ll bits), instead of throwing a cell underflow exception.
  • D705\texttt{D705}LDUXQ\texttt{LDUXQ} (ss llxx ss' 1-1 or ss 00), quiet version of LDUX\texttt{LDUX}.
  • D706\texttt{D706}PLDIXQ\texttt{PLDIXQ} (ss llxx 1-1 or 00), quiet version of PLDIX\texttt{PLDIX}.
  • D707\texttt{D707}PLDUXQ\texttt{PLDUXQ} (ss llxx 1-1 or 00), quiet version of PLDUX\texttt{PLDUX}.
  • D708\texttt{D708} ccccLDI\texttt{LDI} cc+1cc+1 (ssxx ss'), a longer encoding for LDI\texttt{LDI}.
  • D709\texttt{D709} ccccLDU\texttt{LDU} cc+1cc+1 (ssxx ss'), a longer encoding for LDU\texttt{LDU}.
  • D70A\texttt{D70A} ccccPLDI\texttt{PLDI} cc+1cc+1 (ssxx), preloads a signed cc+1cc+1-bit integer from Slice ss.
  • D70B\texttt{D70B} ccccPLDU\texttt{PLDU} cc+1cc+1 (ssxx), preloads an unsigned cc+1cc+1-bit integer from ss.
  • D70C\texttt{D70C} ccccLDIQ\texttt{LDIQ} cc+1cc+1 (ssxx ss' 1-1 or ss 00), a quiet version of LDI\texttt{LDI}.
  • D70D\texttt{D70D} ccccLDUQ\texttt{LDUQ} cc+1cc+1 (ssxx ss' 1-1 or ss 00), a quiet version of LDU\texttt{LDU}.
  • D70E\texttt{D70E} ccccPLDIQ\texttt{PLDIQ} cc+1cc+1 (ssxx 1-1 or 00), a quiet version of PLDI\texttt{PLDI}.
  • D70F\texttt{D70F} ccccPLDUQ\texttt{PLDUQ} cc+1cc+1 (ssxx 1-1 or 00), a quiet version of PLDU\texttt{PLDU}.
  • D714_c\texttt{D714\_}cPLDUZ\texttt{PLDUZ} 32(c+1)32(c+1) (ssss xx), preloads the first 32(c+1)32(c+1) bits of Slice ss into an unsigned integer xx, for 0c70\leq c\leq 7. If ss is shorter than necessary, missing bits are assumed to be zero. This operation is intended to be used along with IFBITJMP\texttt{IFBITJMP} and similar instructions.
  • D718\texttt{D718}LDSLICEX\texttt{LDSLICEX} (ss llss'' ss'), loads the first 0l10230\leq l\leq 1023 bits from Slice ss into a separate Slice ss'', returning the remainder of ss as ss'.
  • D719\texttt{D719}PLDSLICEX\texttt{PLDSLICEX} (ss llss''), returns the first 0l10230\leq l\leq 1023 bits of ss as ss''.
  • D71A\texttt{D71A}LDSLICEXQ\texttt{LDSLICEXQ} (ss llss'' ss' 1-1 or ss 00), a quiet version of LDSLICEX\texttt{LDSLICEX}.
  • D71B\texttt{D71B}PLDSLICEXQ\texttt{PLDSLICEXQ} (ss llss' 1-1 or 00), a quiet version of LDSLICEXQ\texttt{LDSLICEXQ}.
  • D71C\texttt{D71C} ccccLDSLICE\texttt{LDSLICE} cc+1cc+1 (ssss'' ss'), a longer encoding for LDSLICE\texttt{LDSLICE}.
  • D71D\texttt{D71D} ccccPLDSLICE\texttt{PLDSLICE} cc+1cc+1 (ssss''), returns the first 0<cc+12560<cc+1\leq 256 bits of ss as ss''.
  • D71E\texttt{D71E} ccccLDSLICEQ\texttt{LDSLICEQ} cc+1cc+1 (ssss'' ss' 1-1 or ss 00), a quiet version of LDSLICE\texttt{LDSLICE}.
  • D71F\texttt{D71F} ccccPLDSLICEQ\texttt{PLDSLICEQ} cc+1cc+1 (ssss'' 1-1 or 00), a quiet version of PLDSLICE\texttt{PLDSLICE}.
  • D720\texttt{D720}SDCUTFIRST\texttt{SDCUTFIRST} (ss llss'), returns the first 0l10230\leq l\leq 1023 bits of ss. It is equivalent to PLDSLICEX\texttt{PLDSLICEX}.
  • D721\texttt{D721}SDSKIPFIRST\texttt{SDSKIPFIRST} (ss llss'), returns all but the first 0l10230\leq l\leq 1023 bits of ss. It is equivalent to LDSLICEX\texttt{LDSLICEX}; NIP\texttt{NIP}.
  • D722\texttt{D722}SDCUTLAST\texttt{SDCUTLAST} (ss llss'), returns the last 0l10230\leq l\leq 1023 bits of ss.
  • D723\texttt{D723}SDSKIPLAST\texttt{SDSKIPLAST} (ss llss'), returns all but the last 0l10230\leq l\leq 1023 bits of ss.
  • D724\texttt{D724}SDSUBSTR\texttt{SDSUBSTR} (ss ll ll'ss'), returns 0l10230\leq l'\leq 1023 bits of ss starting from offset 0l10230\leq l\leq 1023, thus extracting a bit substring out of the data of ss.
  • D726\texttt{D726}SDBEGINSX\texttt{SDBEGINSX} (ss ss'ss''), checks whether ss begins with (the data bits of) ss', and removes ss' from ss on success. On failure throws a cell deserialization exception. Primitive SDPFXREV\texttt{SDPFXREV} can be considered a quiet version of SDBEGINSX\texttt{SDBEGINSX}.
  • D727\texttt{D727}SDBEGINSXQ\texttt{SDBEGINSXQ} (ss ss'ss'' 1-1 or ss 00), a quiet version of SDBEGINSX\texttt{SDBEGINSX}.
  • D72A_xsss\texttt{D72A\_}xsssSDBEGINS\texttt{SDBEGINS} (ssss''), checks whether ss begins with constant bitstring ssssss of length 8x+38x+3 (with continuation bit assumed), where 0x1270\leq x\leq 127, and removes ssssss from ss on success.
  • D72802\texttt{D72802}SDBEGINS ’0’\texttt{SDBEGINS '0'} (ssss''), checks whether ss begins with a binary zero.
  • D72806\texttt{D72806}SDBEGINS ’1’\texttt{SDBEGINS '1'} (ssss''), checks whether ss begins with a binary one.
  • D72E_xsss\texttt{D72E\_}xsssSDBEGINSQ\texttt{SDBEGINSQ} (ssss'' 1-1 or ss 00), a quiet version of SDBEGINS\texttt{SDBEGINS}.
  • D730\texttt{D730}SCUTFIRST\texttt{SCUTFIRST} (ss ll rrss'), returns the first 0l10230\leq l\leq 1023 bits and first 0r40\leq r\leq 4 references of ss.
  • D731\texttt{D731}SSKIPFIRST\texttt{SSKIPFIRST} (ss ll rrss').
  • D732\texttt{D732}SCUTLAST\texttt{SCUTLAST} (ss ll rrss'), returns the last 0l10230\leq l\leq 1023 data bits and last 0r40\leq r\leq 4 references of ss.
  • D733\texttt{D733}SSKIPLAST\texttt{SSKIPLAST} (ss ll rrss').
  • D734\texttt{D734}SUBSLICE\texttt{SUBSLICE} (ss ll rr ll' rr'ss'), returns 0l10230\leq l'\leq 1023 bits and 0r40\leq r'\leq 4 references from Slice ss, after skipping the first 0l10230\leq l\leq 1023 bits and first 0r40\leq r\leq 4 references.
  • D736\texttt{D736}SPLIT\texttt{SPLIT} (ss ll rrss' ss''), splits the first 0l10230\leq l\leq 1023 data bits and first 0r40\leq r\leq 4 references from ss into ss', returning the remainder of ss as ss''.
  • D737\texttt{D737}SPLITQ\texttt{SPLITQ} (ss ll rrss' ss'' 1-1 or ss 00), a quiet version of SPLIT\texttt{SPLIT}.
  • D739\texttt{D739}XCTOS\texttt{XCTOS} (ccss ??), transforms an ordinary or exotic cell into a Slice, as if it were an ordinary cell. A flag is returned indicating whether cc is exotic. If that be the case, its type can later be deserialized from the first eight bits of ss.
  • D73A\texttt{D73A}XLOAD\texttt{XLOAD} (cccc'), loads an exotic cell cc and returns an ordinary cell cc'. If cc is already ordinary, does nothing. If cc cannot be loaded, throws an exception.
  • D73B\texttt{D73B}XLOADQ\texttt{XLOADQ} (cccc' 1-1 or cc 00), loads an exotic cell cc as XLOAD\texttt{XLOAD}, but returns 0 on failure.
  • D741\texttt{D741}SCHKBITS\texttt{SCHKBITS} (ss ll — ), checks whether there are at least ll data bits in Slice ss. If this is not the case, throws a cell deserialisation (i.e., cell underflow) exception.
  • D742\texttt{D742}SCHKREFS\texttt{SCHKREFS} (ss rr — ), checks whether there are at least rr references in Slice ss.
  • D743\texttt{D743}SCHKBITREFS\texttt{SCHKBITREFS} (ss ll rr — ), checks whether there are at least ll data bits and rr references in Slice ss.
  • D745\texttt{D745}SCHKBITSQ\texttt{SCHKBITSQ} (ss ll??), checks whether there are at least ll data bits in Slice ss.
  • D746\texttt{D746}SCHKREFSQ\texttt{SCHKREFSQ} (ss rr??), checks whether there are at least rr references in Slice ss.
  • D747\texttt{D747}SCHKBITREFSQ\texttt{SCHKBITREFSQ} (ss ll rr??), checks whether there are at least ll data bits and rr references in Slice ss.
  • D748\texttt{D748}PLDREFVAR\texttt{PLDREFVAR} (ss nncc), returns the nn-th cell reference of Slice ss for 0n30\leq n\leq 3.
  • D749\texttt{D749}SBITS\texttt{SBITS} (ssll), returns the number of data bits in Slice ss.
  • D74A\texttt{D74A}SREFS\texttt{SREFS} (ssrr), returns the number of references in Slice ss.
  • D74B\texttt{D74B}SBITREFS\texttt{SBITREFS} (ssll rr), returns both the number of data bits and the number of references in ss.
  • D74E_n\texttt{D74E\_}nPLDREFIDX\texttt{PLDREFIDX} nn (sscc), returns the nn-th cell reference of Slice ss, where 0n30\leq n\leq 3.
  • D74C\texttt{D74C}PLDREF\texttt{PLDREF} (sscc), preloads the first cell reference of a Slice.
  • D750\texttt{D750}LDILE4\texttt{LDILE4} (ssxx ss'), loads a little-endian signed 32-bit integer.
  • D751\texttt{D751}LDULE4\texttt{LDULE4} (ssxx ss'), loads a little-endian unsigned 32-bit integer.
  • D752\texttt{D752}LDILE8\texttt{LDILE8} (ssxx ss'), loads a little-endian signed 64-bit integer.
  • D753\texttt{D753}LDULE8\texttt{LDULE8} (ssxx ss'), loads a little-endian unsigned 64-bit integer.
  • D754\texttt{D754}PLDILE4\texttt{PLDILE4} (ssxx), preloads a little-endian signed 32-bit integer.
  • D755\texttt{D755}PLDULE4\texttt{PLDULE4} (ssxx), preloads a little-endian unsigned 32-bit integer.
  • D756\texttt{D756}PLDILE8\texttt{PLDILE8} (ssxx), preloads a little-endian signed 64-bit integer.
  • D757\texttt{D757}PLDULE8\texttt{PLDULE8} (ssxx), preloads a little-endian unsigned 64-bit integer.
  • D758\texttt{D758}LDILE4Q\texttt{LDILE4Q} (ssxx ss' 1-1 or ss 00), quietly loads a little-endian signed 32-bit integer.
  • D759\texttt{D759}LDULE4Q\texttt{LDULE4Q} (ssxx ss' 1-1 or ss 00), quietly loads a little-endian unsigned 32-bit integer.
  • D75A\texttt{D75A}LDILE8Q\texttt{LDILE8Q} (ssxx ss' 1-1 or ss 00), quietly loads a little-endian signed 64-bit integer.
  • D75B\texttt{D75B}LDULE8Q\texttt{LDULE8Q} (ssxx ss' 1-1 or ss 00), quietly loads a little-endian unsigned 64-bit integer.
  • D75C\texttt{D75C}PLDILE4Q\texttt{PLDILE4Q} (ssxx 1-1 or 00), quietly preloads a little-endian signed 32-bit integer.
  • D75D\texttt{D75D}PLDULE4Q\texttt{PLDULE4Q} (ssxx 1-1 or 00), quietly preloads a little-endian unsigned 32-bit integer.
  • D75E\texttt{D75E}PLDILE8Q\texttt{PLDILE8Q} (ssxx 1-1 or 00), quietly preloads a little-endian signed 64-bit integer.
  • D75F\texttt{D75F}PLDULE8Q\texttt{PLDULE8Q} (ssxx 1-1 or 00), quietly preloads a little-endian unsigned 64-bit integer.
  • D760\texttt{D760}LDZEROES\texttt{LDZEROES} (ssnn ss'), returns the count nn of leading zero bits in ss, and removes these bits from ss.
  • D761\texttt{D761}LDONES\texttt{LDONES} (ssnn ss'), returns the count nn of leading one bits in ss, and removes these bits from ss.
  • D762\texttt{D762}LDSAME\texttt{LDSAME} (ss xxnn ss'), returns the count nn of leading bits equal to 0x10\leq x\leq 1 in ss, and removes these bits from ss.
  • D764\texttt{D764}SDEPTH\texttt{SDEPTH} (ssxx), returns the depth of Slice ss. If ss has no references, then x=0x=0; otherwise xx is one plus the maximum of depths of cells referred to from ss.
  • D765\texttt{D765}CDEPTH\texttt{CDEPTH} (ccxx), returns the depth of Cell cc. If cc has no references, then x=0x=0; otherwise xx is one plus the maximum of depths of cells referred to from cc. If cc is a Null instead of a Cell, returns zero.

A.8 Continuation and control flow primitives

A.8.1. Unconditional control flow primitives

  • D8\texttt{D8}EXECUTE\texttt{EXECUTE} or CALLX\texttt{CALLX} (cc - ), calls or executes continuation cc (i.e., ccc0cc\texttt{cc}\leftarrow c\circ_0\texttt{cc}).
  • D9\texttt{D9}JMPX\texttt{JMPX} (cc - ), jumps, or transfers control, to continuation cc (i.e., ccc0c0\texttt{cc}\leftarrow c\circ_0\texttt{c0}, or rather cc(c0c0)1c1\texttt{cc}\leftarrow(c\circ_0\texttt{c0})\circ_1\texttt{c1}). The remainder of the previous current continuation cc\texttt{cc} is discarded.
  • DApr\texttt{DA}prCALLXARGS\texttt{CALLXARGS} pp,rr (cc - ), calls continuation cc with pp parameters and expecting rr return values, 0p150\leq p\leq15, 0r150\leq r\leq 15.
  • DB0p\texttt{DB0}pCALLXARGS\texttt{CALLXARGS} pp,1-1 (cc - ), calls continuation cc with 0p150\leq p\leq15 parameters, expecting an arbitrary number of return values.
  • DB1p\texttt{DB1}pJMPXARGS\texttt{JMPXARGS} pp (cc - ), jumps to continuation cc, passing only the top 0p150\leq p\leq 15 values from the current stack to it (the remainder of the current stack is discarded).
  • DB2r\texttt{DB2}rRETARGS\texttt{RETARGS} rr, returns to c0\texttt{c0}, with 0r150\leq r\leq 15 return values taken from the current stack.
  • DB30\texttt{DB30}RET\texttt{RET} or RETTRUE\texttt{RETTRUE}, returns to the continuation at c0\texttt{c0} (i.e., performs ccc0\texttt{cc}\leftarrow\texttt{c0}). The remainder of the current continuation cc\texttt{cc} is discarded. Approximately equivalent to PUSH c0\texttt{PUSH c0}; JMPX\texttt{JMPX}.
  • DB31\texttt{DB31}RETALT\texttt{RETALT} or RETFALSE\texttt{RETFALSE}, returns to the continuation at c1\texttt{c1} (i.e., ccc1\texttt{cc}\leftarrow\texttt{c1}). Approximately equivalent to PUSH c1\texttt{PUSH c1}; JMPX\texttt{JMPX}.
  • DB32\texttt{DB32}BRANCH\texttt{BRANCH} or RETBOOL\texttt{RETBOOL} (ff - ), performs RETTRUE\texttt{RETTRUE} if integer f0f\neq 0, or RETFALSE\texttt{RETFALSE} if f=0f=0.
  • DB34\texttt{DB34}CALLCC\texttt{CALLCC} (cc - ), call with current continuation, transfers control to cc, pushing the old value of cc\texttt{cc} into cc‘s stack (instead of discarding it or writing it into new c0\texttt{c0}).
  • DB35\texttt{DB35}JMPXDATA\texttt{JMPXDATA} (cc - ), similar to CALLCC\texttt{CALLCC}, but the remainder of the current continuation (the old value of cc\texttt{cc}) is converted into a Slice before pushing it into the stack of cc.
  • DB36pr\texttt{DB36}prCALLCCARGS\texttt{CALLCCARGS} pp,rr (cc - ), similar to CALLXARGS\texttt{CALLXARGS}, but pushes the old value of cc\texttt{cc} (along with the top 0p150\leq p\leq 15 values from the original stack) into the stack of newly-invoked continuation cc, setting cc.nargs\texttt{cc.nargs} to 1r14-1\leq r\leq 14.
  • DB38\texttt{DB38}CALLXVARARGS\texttt{CALLXVARARGS} (cc pp rr - ), similar to CALLXARGS\texttt{CALLXARGS}, but takes 1p,r254-1\leq p,r\leq254 from the stack. The next three operations also take pp and rr from the stack, both in the range 1254-1\ldots254.
  • DB39\texttt{DB39}RETVARARGS\texttt{RETVARARGS} (pp rr - ), similar to RETARGS\texttt{RETARGS}.
  • DB3A\texttt{DB3A}JMPXVARARGS\texttt{JMPXVARARGS} (cc pp rr - ), similar to JMPXARGS\texttt{JMPXARGS}.
  • DB3B\texttt{DB3B}CALLCCVARARGS\texttt{CALLCCVARARGS} (cc pp rr - ), similar to CALLCCARGS\texttt{CALLCCARGS}.
  • DB3C\texttt{DB3C}CALLREF\texttt{CALLREF}, equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; CALLX\texttt{CALLX}.
  • DB3D\texttt{DB3D}JMPREF\texttt{JMPREF}, equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; JMPX\texttt{JMPX}.
  • DB3E\texttt{DB3E}JMPREFDATA\texttt{JMPREFDATA}, equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; JMPXDATA\texttt{JMPXDATA}.
  • DB3F\texttt{DB3F}RETDATA\texttt{RETDATA}, equivalent to PUSH c0\texttt{PUSH c0}; JMPXDATA\texttt{JMPXDATA}. In this way, the remainder of the current continuation is converted into a Slice and returned to the caller.

A.8.2. Conditional control flow primitives

  • DC\texttt{DC}IFRET\texttt{IFRET} (ff - ), performs a RET\texttt{RET}, but only if integer ff is non-zero. If ff is a NaN\texttt{NaN}, throws an integer overflow exception.
  • DD\texttt{DD}IFNOTRET\texttt{IFNOTRET} (ff - ), performs a RET\texttt{RET}, but only if integer ff is zero.
  • DE\texttt{DE}IF\texttt{IF} (ff cc - ), performs EXECUTE\texttt{EXECUTE} for cc (i.e., executes cc), but only if integer ff is non-zero. Otherwise simply discards both values.
  • DF\texttt{DF}IFNOT\texttt{IFNOT} (ff cc - ), executes continuation cc, but only if integer ff is zero. Otherwise simply discards both values.
  • E0\texttt{E0}IFJMP\texttt{IFJMP} (ff cc - ), jumps to cc (similarly to JMPX\texttt{JMPX}), but only if ff is non-zero.
  • E1\texttt{E1}IFNOTJMP\texttt{IFNOTJMP} (ff cc - ), jumps to cc (similarly to JMPX\texttt{JMPX}), but only if ff is zero.
  • E2\texttt{E2}IFELSE\texttt{IFELSE} (ff cc cc' - ), if integer ff is non-zero, executes cc, otherwise executes cc'. Equivalent to CONDSELCHK\texttt{CONDSELCHK}; EXECUTE\texttt{EXECUTE}.
  • E300\texttt{E300}IFREF\texttt{IFREF} (ff - ), equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; IF\texttt{IF}, with the optimization that the cell reference is not actually loaded into a Slice and then converted into an ordinary Continuation if f=0f=0. Similar remarks apply to the next three primitives.
  • E301\texttt{E301}IFNOTREF\texttt{IFNOTREF} (ff - ), equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; IFNOT\texttt{IFNOT}.
  • E302\texttt{E302}IFJMPREF\texttt{IFJMPREF} (ff - ), equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; IFJMP\texttt{IFJMP}.
  • E303\texttt{E303}IFNOTJMPREF\texttt{IFNOTJMPREF} (ff - ), equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; IFNOTJMP\texttt{IFNOTJMP}.
  • E304\texttt{E304}CONDSEL\texttt{CONDSEL} (ff xx yy - xx or yy), if integer ff is non-zero, returns xx, otherwise returns yy. Notice that no type checks are performed on xx and yy; as such, it is more like a conditional stack operation. Roughly equivalent to ROT\texttt{ROT}; ISZERO\texttt{ISZERO}; INC\texttt{INC}; ROLLX\texttt{ROLLX}; NIP\texttt{NIP}.
  • E305\texttt{E305}CONDSELCHK\texttt{CONDSELCHK} (ff xx yy - xx or yy), same as CONDSEL\texttt{CONDSEL}, but first checks whether xx and yy have the same type.
  • E308\texttt{E308}IFRETALT\texttt{IFRETALT} (ff -), performs RETALT\texttt{RETALT} if integer f0f\neq0.
  • E309\texttt{E309}IFNOTRETALT\texttt{IFNOTRETALT} (ff -), performs RETALT\texttt{RETALT} if integer f=0f=0.
  • E30D\texttt{E30D}IFREFELSE\texttt{IFREFELSE} (ff cc -), equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; SWAP\texttt{SWAP}; IFELSE\texttt{IFELSE}, with the optimization that the cell reference is not actually loaded into a Slice and then converted into an ordinary Continuation if f=0f=0. Similar remarks apply to the next two primitives: Cells are converted into Continuations only when necessary.
  • E30E\texttt{E30E}IFELSEREF\texttt{IFELSEREF} (ff cc -), equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; IFELSE\texttt{IFELSE}.
  • E30F\texttt{E30F}IFREFELSEREF\texttt{IFREFELSEREF} (ff -), equivalent to PUSHREFCONT\texttt{PUSHREFCONT}; PUSHREFCONT\texttt{PUSHREFCONT}; IFELSE\texttt{IFELSE}.
  • E310\texttt{E310}E31F\texttt{E31F} — reserved for loops with break operators, cf. A.8.4 below.
  • E39_n\texttt{E39\_}nIFBITJMP\texttt{IFBITJMP} nn (xx cc - xx), checks whether bit 0n310\leq n\leq 31 is set in integer xx, and if so, performs JMPX\texttt{JMPX} to continuation cc. Value xx is left in the stack.
  • E3B_n\texttt{E3B\_}nIFNBITJMP\texttt{IFNBITJMP} nn (xx cc - xx), jumps to cc if bit 0n310\leq n\leq 31 is not set in integer xx.
  • E3D_n\texttt{E3D\_}nIFBITJMPREF\texttt{IFBITJMPREF} nn (xx - xx), performs a JMPREF\texttt{JMPREF} if bit 0n310\leq n\leq 31 is set in integer xx.
  • E3F_n\texttt{E3F\_}nIFNBITJMPREF\texttt{IFNBITJMPREF} nn (xx - xx), performs a JMPREF\texttt{JMPREF} if bit 0n310\leq n\leq 31 is not set in integer xx.

A.8.3. Control flow primitives: loops

Most of the loop primitives listed below are implemented with the aid of extraordinary continuations, such as ec_until\texttt{ec\_until} (cf. 4.1.5), with the loop body and the original current continuation cc\texttt{cc} stored as the arguments to this extraordinary continuation. Typically a suitable extraordinary continuation is constructed, and then saved into the loop body continuation savelist as c0\texttt{c0}; after that, the modified loop body continuation is loaded into cc\texttt{cc} and executed in the usual fashion. All of these loop primitives have *BRK\texttt{*BRK} versions, adapted for breaking out of a loop; they additionally set c1\texttt{c1} to the original current continuation (or original c0\texttt{c0} for *ENDBRK\texttt{*ENDBRK} versions), and save the old c1\texttt{c1} into the savelist of the original current continuation (or of the original c0\texttt{c0} for *ENDBRK\texttt{*ENDBRK} versions).
  • E4\texttt{E4}REPEAT\texttt{REPEAT} (nn cc - ), executes continuation cc nn times, if integer nn is non-negative. If n231n\geq2^{31} or n<231n<-2^{31}, generates a range check exception. Notice that a RET\texttt{RET} inside the code of cc works as a continue\texttt{continue}, not as a break\texttt{break}. One should use either alternative (experimental) loops or alternative RETALT\texttt{RETALT} (along with a SETEXITALT\texttt{SETEXITALT} before the loop) to break\texttt{break} out of a loop.
  • E5\texttt{E5}REPEATEND\texttt{REPEATEND} (nn - ), similar to REPEAT\texttt{REPEAT}, but it is applied to the current continuation cc\texttt{cc}.
  • E6\texttt{E6}UNTIL\texttt{UNTIL} (cc - ), executes continuation cc, then pops an integer xx from the resulting stack. If xx is zero, performs another iteration of this loop. The actual implementation of this primitive involves an extraordinary continuation ec_until\texttt{ec\_until} (cf. 4.1.5) with its arguments set to the body of the loop (continuation cc) and the original current continuation cc\texttt{cc}. This extraordinary continuation is then saved into the savelist of cc as cc.c0\texttt{c0} and the modified cc is then executed. The other loop primitives are implemented similarly with the aid of suitable extraordinary continuations.
  • E7\texttt{E7}UNTILEND\texttt{UNTILEND} ( - ), similar to UNTIL\texttt{UNTIL}, but executes the current continuation cc\texttt{cc} in a loop. When the loop exit condition is satisfied, performs a RET\texttt{RET}.
  • E8\texttt{E8}WHILE\texttt{WHILE} (cc' cc - ), executes cc' and pops an integer xx from the resulting stack. If xx is zero, exists the loop and transfers control to the original cc\texttt{cc}. If xx is non-zero, executes cc, and then begins a new iteration.
  • E9\texttt{E9}WHILEEND\texttt{WHILEEND} (cc' - ), similar to WHILE\texttt{WHILE}, but uses the current continuation cc\texttt{cc} as the loop body.
  • EA\texttt{EA}AGAIN\texttt{AGAIN} (cc - ), similar to REPEAT\texttt{REPEAT}, but executes cc infinitely many times. A RET\texttt{RET} only begins a new iteration of the infinite loop, which can be exited only by an exception, or a RETALT\texttt{RETALT} (or an explicit JMPX\texttt{JMPX}).
  • EB\texttt{EB}AGAINEND\texttt{AGAINEND} ( - ), similar to AGAIN\texttt{AGAIN}, but performed with respect to the current continuation cc\texttt{cc}.
  • E314\texttt{E314}REPEATBRK\texttt{REPEATBRK} (nn cc - ), similar to REPEAT\texttt{REPEAT}, but also sets c1\texttt{c1} to the original cc\texttt{cc} after saving the old value of c1\texttt{c1} into the savelist of the original cc\texttt{cc}. In this way RETALT\texttt{RETALT} could be used to break out of the loop body.
  • E315\texttt{E315}REPEATENDBRK\texttt{REPEATENDBRK} (nn - ), similar to REPEATEND\texttt{REPEATEND}, but also sets c1\texttt{c1} to the original c0\texttt{c0} after saving the old value of c1\texttt{c1} into the savelist of the original c0\texttt{c0}. Equivalent to SAMEALTSAVE\texttt{SAMEALTSAVE}; REPEATEND\texttt{REPEATEND}.
  • E316\texttt{E316}UNTILBRK\texttt{UNTILBRK} (cc - ), similar to UNTIL\texttt{UNTIL}, but also modifies c1\texttt{c1} in the same way as REPEATBRK\texttt{REPEATBRK}.
  • E317\texttt{E317}UNTILENDBRK\texttt{UNTILENDBRK} ( - ), equivalent to SAMEALTSAVE\texttt{SAMEALTSAVE}; UNTILEND\texttt{UNTILEND}.
  • E318\texttt{E318}WHILEBRK\texttt{WHILEBRK} (cc' cc - ), similar to WHILE\texttt{WHILE}, but also modifies c1\texttt{c1} in the same way as REPEATBRK\texttt{REPEATBRK}.
  • E319\texttt{E319}WHILEENDBRK\texttt{WHILEENDBRK} (cc - ), equivalent to SAMEALTSAVE\texttt{SAMEALTSAVE}; WHILEEND\texttt{WHILEEND}.
  • E31A\texttt{E31A}AGAINBRK\texttt{AGAINBRK} (cc - ), similar to AGAIN\texttt{AGAIN}, but also modifies c1\texttt{c1} in the same way as REPEATBRK\texttt{REPEATBRK}.
  • E31B\texttt{E31B}AGAINENDBRK\texttt{AGAINENDBRK} ( - ), equivalent to SAMEALTSAVE\texttt{SAMEALTSAVE}; AGAINEND\texttt{AGAINEND}.

A.8.4. Manipulating the stack of continuations

  • ECrn\texttt{EC}rnSETCONTARGS\texttt{SETCONTARGS} rr,nn (x1x_1 x2xrx_2\ldots x_r cc - cc'), similar to SETCONTARGS\texttt{SETCONTARGS} rr, but sets cc.nargs\texttt{nargs} to the final size of the stack of cc' plus nn. In other words, transforms cc into a closure or a partially applied function, with 0n140\leq n\leq 14 arguments missing.
  • EC0n\texttt{EC0}nSETNUMARGS\texttt{SETNUMARGS} nn or SETCONTARGS\texttt{SETCONTARGS} 00,nn (cc - cc'), sets cc.nargs\texttt{nargs} to nn plus the current depth of cc‘s stack, where 0n140\leq n\leq 14. If cc.nargs\texttt{nargs} is already set to a non-negative value, does nothing.
  • ECrF\texttt{EC}r\texttt{F}SETCONTARGS\texttt{SETCONTARGS} rr or SETCONTARGS\texttt{SETCONTARGS} rr,1-1 (x1x_1 x2xrx_2\ldots x_r cc - cc'), pushes 0r150\leq r\leq 15 values x1xrx_1\ldots x_r into the stack of (a copy of) the continuation cc, starting with x1x_1. If the final depth of cc‘s stack turns out to be greater than cc.nargs\texttt{nargs}, a stack overflow exception is generated.
  • ED0p\texttt{ED0}pRETURNARGS\texttt{RETURNARGS} pp ( - ), leaves only the top 0p150\leq p\leq 15 values in the current stack (somewhat similarly to ONLYTOPX\texttt{ONLYTOPX}), with all the unused bottom values not discarded, but saved into continuation c0\texttt{c0} in the same way as SETCONTARGS\texttt{SETCONTARGS} does.
  • ED10\texttt{ED10}RETURNVARARGS\texttt{RETURNVARARGS} (pp - ), similar to RETURNARGS\texttt{RETURNARGS}, but with Integer 0p2550\leq p\leq 255 taken from the stack.
  • ED11\texttt{ED11}SETCONTVARARGS\texttt{SETCONTVARARGS} (x1x_1 x2xrx_2\ldots x_r cc rr nn - cc'), similar to SETCONTARGS\texttt{SETCONTARGS}, but with 0r2550\leq r\leq 255 and 1n255-1\leq n\leq 255 taken from the stack.
  • ED12\texttt{ED12}SETNUMVARARGS\texttt{SETNUMVARARGS} (cc nn - cc'), where 1n255-1\leq n\leq 255. If n=1n=-1, this operation does nothing (c=cc'=c). Otherwise its action is similar to SETNUMARGS\texttt{SETNUMARGS} nn, but with nn taken from the stack.

A.8.5. Creating simple continuations and closures

  • ED1E\texttt{ED1E}BLESS\texttt{BLESS} (ss - cc), transforms a Slice ss into a simple ordinary continuation cc, with cc.code=s\texttt{code}=s and an empty stack and savelist.
  • ED1F\texttt{ED1F}BLESSVARARGS\texttt{BLESSVARARGS} (x1xrx_1\ldots x_r ss rr nn - cc), equivalent to ROT\texttt{ROT}; BLESS\texttt{BLESS}; ROTREV\texttt{ROTREV}; SETCONTVARARGS\texttt{SETCONTVARARGS}.
  • EErn\texttt{EE}rnBLESSARGS\texttt{BLESSARGS} r,nr,n (x1xrx_1\ldots x_r ss - cc), where 0r150\leq r\leq 15, 1n14-1\leq n\leq 14, equivalent to BLESS\texttt{BLESS}; SETCONTARGS\texttt{SETCONTARGS} r,nr,n. The value of nn is represented inside the instruction by the 4-bit integer nmod16n\bmod16.
  • EE0n\texttt{EE0}nBLESSNUMARGS\texttt{BLESSNUMARGS} nn or BLESSARGS 0\texttt{BLESSARGS 0},nn (ss - cc), also transforms a Slice ss into a Continuation cc, but sets cc.nargs\texttt{nargs} to 0n140\leq n\leq 14.

A.8.6. Operations with continuation savelists and control registers

  • ED4i\texttt{ED4}iPUSH c(i)\texttt{PUSH c}(i) or PUSHCTR c(i)\texttt{PUSHCTR c}(i) ( - xx), pushes the current value of control register c(i)\texttt{c}(i). If the control register is not supported in the current codepage, or if it does not have a value, an exception is triggered.
  • ED44\texttt{ED44}PUSH c4\texttt{PUSH c4} or PUSHROOT\texttt{PUSHROOT}, pushes the “global data root” cell reference, thus enabling access to persistent smart-contract data.
  • ED5i\texttt{ED5}iPOP c(i)\texttt{POP c}(i) or POPCTR c(i)\texttt{POPCTR c}(i) (xx - ), pops a value xx from the stack and stores it into control register c(i)\texttt{c}(i), if supported in the current codepage. Notice that if a control register accepts only values of a specific type, a type-checking exception may occur.
  • ED54\texttt{ED54}POP c4\texttt{POP c4} or POPROOT\texttt{POPROOT}, sets the “global data root” cell reference, thus allowing modification of persistent smart-contract data.
  • ED6i\texttt{ED6}iSETCONT c(i)\texttt{SETCONT c}(i) or SETCONTCTR c(i)\texttt{SETCONTCTR c}(i) (xx cc - cc'), stores xx into the savelist of continuation cc as c(i)\texttt{c}(i), and returns the resulting continuation cc'. Almost all operations with continuations may be expressed in terms of SETCONTCTR\texttt{SETCONTCTR}, POPCTR\texttt{POPCTR}, and PUSHCTR\texttt{PUSHCTR}.
  • ED7i\texttt{ED7}iSETRETCTR c(i)\texttt{SETRETCTR c}(i) (xx - ), equivalent to PUSH c0\texttt{PUSH c0}; SETCONTCTR c(i)\texttt{SETCONTCTR c}(i); POP c0\texttt{POP c0}.
  • ED8i\texttt{ED8}iSETALTCTR c(i)\texttt{SETALTCTR c}(i) (xx - ), equivalent to PUSH c1\texttt{PUSH c1}; SETCONTCTR c(i)\texttt{SETCONTCTR c}(i); POP c0\texttt{POP c0}.
  • ED9i\texttt{ED9}iPOPSAVE c(i)\texttt{POPSAVE c}(i) or POPCTRSAVE c(i)\texttt{POPCTRSAVE c}(i) (xx -), similar to POP c(i)\texttt{POP c}(i), but also saves the old value of c(i)\texttt{c}(i) into continuation c0\texttt{c0}. Equivalent (up to exceptions) to SAVECTR c(i)\texttt{SAVECTR c}(i); POP c(i)\texttt{POP c}(i).
  • EDAi\texttt{EDA}iSAVE c(i)\texttt{SAVE c}(i) or SAVECTR c(i)\texttt{SAVECTR c}(i) ( - ), saves the current value of c(i)\texttt{c}(i) into the savelist of continuation c0\texttt{c0}. If an entry for c(i)\texttt{c}(i) is already present in the savelist of c0\texttt{c0}, nothing is done. Equivalent to PUSH c(i)\texttt{PUSH c}(i); SETRETCTR c(i)\texttt{SETRETCTR c}(i).
  • EDBi\texttt{EDB}iSAVEALT c(i)\texttt{SAVEALT c}(i) or SAVEALTCTR c(i)\texttt{SAVEALTCTR c}(i) ( - ), similar to SAVE c(i)\texttt{SAVE c}(i), but saves the current value of c(i)\texttt{c}(i) into the savelist of c1\texttt{c1}, not c0\texttt{c0}.
  • EDCi\texttt{EDC}iSAVEBOTH c(i)\texttt{SAVEBOTH c}(i) or SAVEBOTHCTR c(i)\texttt{SAVEBOTHCTR c}(i) ( - ), equivalent to DUP\texttt{DUP}; SAVE c(i)\texttt{SAVE c}(i); SAVEALT c(i)\texttt{SAVEALT c}(i).
  • EDE0\texttt{EDE0}PUSHCTRX\texttt{PUSHCTRX} (ii - xx), similar to PUSHCTR c(i)\texttt{PUSHCTR c}(i), but with ii, 0i2550\leq i\leq255, taken from the stack. Notice that this primitive is one of the few “exotic” primitives, which are not polymorphic like stack manipulation primitives, and at the same time do not have well-defined types of parameters and return values, because the type of xx depends on ii.
  • EDE1\texttt{EDE1}POPCTRX\texttt{POPCTRX} (xx ii - ), similar to POPCTR c(i)\texttt{POPCTR c}(i), but with 0i2550\leq i\leq255 from the stack.
  • EDE2\texttt{EDE2}SETCONTCTRX\texttt{SETCONTCTRX} (xx cc ii - cc'), similar to SETCONTCTR c(i)\texttt{SETCONTCTR c}(i), but with 0i2550\leq i\leq 255 from the stack.
  • EDF0\texttt{EDF0}COMPOS\texttt{COMPOS} or BOOLAND\texttt{BOOLAND} (cc cc' - cc''), computes the composition c0cc\circ_0c', which has the meaning of “perform cc, and, if successful, perform cc'” (if cc is a boolean circuit) or simply “perform cc, then cc'”. Equivalent to SWAP\texttt{SWAP}; SETCONT c0\texttt{SETCONT c0}.
  • EDF1\texttt{EDF1}COMPOSALT\texttt{COMPOSALT} or BOOLOR\texttt{BOOLOR} (cc cc' - cc''), computes the alternative composition c1cc\circ_1 c', which has the meaning of “perform cc, and, if not successful, perform cc'” (if cc is a boolean circuit). Equivalent to SWAP\texttt{SWAP}; SETCONT c1\texttt{SETCONT c1}.
  • EDF2\texttt{EDF2}COMPOSBOTH\texttt{COMPOSBOTH} (cc cc' - cc''), computes (c0c)1c(c\circ_0c')\circ_1c', which has the meaning of “compute boolean circuit cc, then compute cc', regardless of the result of cc”.
  • EDF3\texttt{EDF3}ATEXIT\texttt{ATEXIT} (cc - ), sets c0c0c0\texttt{c0}\leftarrow c\circ_0\texttt{c0}. In other words, cc will be executed before exiting current subroutine.
  • EDF4\texttt{EDF4}ATEXITALT\texttt{ATEXITALT} (cc - ), sets c1c1c1\texttt{c1}\leftarrow c\circ_1\texttt{c1}. In other words, cc will be executed before exiting current subroutine by its alternative return path.
  • EDF5\texttt{EDF5}SETEXITALT\texttt{SETEXITALT} (cc - ), sets c1(c0c0)1c1\texttt{c1}\leftarrow (c\circ_0\texttt{c0})\circ_1\texttt{c1}. In this way, a subsequent RETALT\texttt{RETALT} will first execute cc, then transfer control to the original c0\texttt{c0}. This can be used, for instance, to exit from nested loops.
  • EDF6\texttt{EDF6}THENRET\texttt{THENRET} (cc - cc'), computes c:=c0c0c':=c\circ_0\texttt{c0}
  • EDF7\texttt{EDF7}THENRETALT\texttt{THENRETALT} (cc - cc'), computes c:=c0c1c':=c\circ_0\texttt{c1}
  • EDF8\texttt{EDF8}INVERT\texttt{INVERT} ( - ), interchanges c0\texttt{c0} and c1\texttt{c1}.
  • EDF9\texttt{EDF9}BOOLEVAL\texttt{BOOLEVAL} (cc - ??), performs cc(c0((PUSH1)0cc))1((PUSH0)0cc)\texttt{cc}\leftarrow \bigl(c\circ_0((\texttt{PUSH}\,-1)\circ_0\texttt{cc})\bigr)\circ_1((\texttt{PUSH}\,0)\circ_0\texttt{cc}). If cc represents a boolean circuit, the net effect is to evaluate it and push either 1-1 or 00 into the stack before continuing.
  • EDFA\texttt{EDFA}SAMEALT\texttt{SAMEALT} ( - ), sets c1:=c0c_1:=c_0. Equivalent to PUSH c0\texttt{PUSH c0}; POP c1\texttt{POP c1}.
  • EDFB\texttt{EDFB}SAMEALTSAVE\texttt{SAMEALTSAVE} ( - ), sets c1:=c0c_1:=c_0, but first saves the old value of c1c_1 into the savelist of c0c_0. Equivalent to SAVE c1\texttt{SAVE c1}; SAMEALT\texttt{SAMEALT}.
  • EErn\texttt{EE}rnBLESSARGS\texttt{BLESSARGS} r,nr,n (x1xrx_1\ldots x_r sscc), described in A.8.4.

A.8.7. Dictionary subroutine calls and jumps

  • F0n\texttt{F0}nCALL\texttt{CALL} nn or CALLDICT\texttt{CALLDICT} nn ( - nn), calls the continuation in c3\texttt{c3}, pushing integer 0n2550\leq n\leq 255 into its stack as an argument. Approximately equivalent to PUSHINT\texttt{PUSHINT} nn; PUSH c3\texttt{PUSH c3}; EXECUTE\texttt{EXECUTE}.
  • F12_n\texttt{F12\_}nCALL\texttt{CALL} nn for 0n<2140\leq n<2^{14} ( - nn), an encoding of CALL\texttt{CALL} nn for larger values of nn.
  • F16_n\texttt{F16\_}nJMP\texttt{JMP} nn or JMPDICT\texttt{JMPDICT} nn ( - nn), jumps to the continuation in c3\texttt{c3}, pushing integer 0n<2140\leq n<2^{14} as its argument. Approximately equivalent to PUSHINT\texttt{PUSHINT} nn; PUSH c3\texttt{PUSH c3}; JMPX\texttt{JMPX}.
  • F1A_n\texttt{F1A\_}nPREPARE\texttt{PREPARE} nn or PREPAREDICT\texttt{PREPAREDICT} nn ( - nn cc), equivalent to PUSHINT\texttt{PUSHINT} nn; PUSH c3\texttt{PUSH c3}, for 0n<2140\leq n<2^{14}. In this way, CALL\texttt{CALL} nn is approximately equivalent to PREPARE\texttt{PREPARE} nn; EXECUTE\texttt{EXECUTE}, and JMP\texttt{JMP} nn is approximately equivalent to PREPARE\texttt{PREPARE} nn; JMPX\texttt{JMPX}. One might use, for instance, CALLARGS\texttt{CALLARGS} or CALLCC\texttt{CALLCC} instead of EXECUTE\texttt{EXECUTE} here.

A.9 Exception generating and handling primitives

A.9.1. Throwing exceptions

  • F22_nn\texttt{F22\_}nnTHROW\texttt{THROW} nnnn ( - 00 nnnn), throws exception 0nn630\leq nn\leq 63 with parameter zero. In other words, it transfers control to the continuation in c2\texttt{c2}, pushing 00 and nnnn into its stack, and discarding the old stack altogether.
  • F26_nn\texttt{F26\_}nnTHROWIF\texttt{THROWIF} nnnn (ff - ), throws exception 0nn630\leq nn\leq 63 with parameter zero only if integer f0f\neq0.
  • F2A_nn\texttt{F2A\_}nnTHROWIFNOT\texttt{THROWIFNOT} nnnn (ff - ), throws exception 0nn630\leq nn\leq 63 with parameter zero only if integer f=0f=0.
  • F2C4_nn\texttt{F2C4\_}nnTHROW\texttt{THROW} nnnn for 0nn<2110\leq nn<2^{11}, an encoding of THROW\texttt{THROW} nnnn for larger values of nnnn.
  • F2CC_nn\texttt{F2CC\_}nnTHROWARG\texttt{THROWARG} nnnn (xx - xx nnnn), throws exception 0nn<2110\leq nn<2^{11} with parameter xx, by copying xx and nnnn into the stack of c2\texttt{c2} and transferring control to c2\texttt{c2}.
  • F2D4_nn\texttt{F2D4\_}nnTHROWIF\texttt{THROWIF} nnnn (ff - ) for 0nn<2110\leq nn<2^{11}.
  • F2DC_nn\texttt{F2DC\_}nnTHROWARGIF\texttt{THROWARGIF} nnnn (xx ff - ), throws exception 0nn<2110\leq nn<2^{11} with parameter xx only if integer f0f\neq0.
  • F2E4_nn\texttt{F2E4\_}nnTHROWIFNOT\texttt{THROWIFNOT} nnnn (ff - ) for 0nn<2110\leq nn<2^{11}.
  • F2EC_nn\texttt{F2EC\_}nnTHROWARGIFNOT\texttt{THROWARGIFNOT} nnnn (xx ff - ), throws exception 0nn<2110\leq nn<2^{11} with parameter xx only if integer f=0f=0.
  • F2F0\texttt{F2F0}THROWANY\texttt{THROWANY} (nn - 00 nn), throws exception 0n<2160\leq n<2^{16} with parameter zero. Approximately equivalent to PUSHINT 0\texttt{PUSHINT 0}; SWAP\texttt{SWAP}; THROWARGANY\texttt{THROWARGANY}.
  • F2F1\texttt{F2F1}THROWARGANY\texttt{THROWARGANY} (xx nn - xx nn), throws exception 0n<2160\leq n<2^{16} with parameter xx, transferring control to the continuation in c2\texttt{c2}. Approximately equivalent to PUSH c2\texttt{PUSH c2}; JMPXARGS 2\texttt{JMPXARGS 2}.
  • F2F2\texttt{F2F2}THROWANYIF\texttt{THROWANYIF} (nn ff - ), throws exception 0n<2160\leq n<2^{16} with parameter zero only if f0f\neq0.
  • F2F3\texttt{F2F3}THROWARGANYIF\texttt{THROWARGANYIF} (xx nn ff - ), throws exception 0n<2160\leq n<2^{16} with parameter xx only if f0f\neq0.
  • F2F4\texttt{F2F4}THROWANYIFNOT\texttt{THROWANYIFNOT} (nn ff - ), throws exception 0n<2160\leq n<2^{16} with parameter zero only if f=0f=0.
  • F2F5\texttt{F2F5}THROWARGANYIFNOT\texttt{THROWARGANYIFNOT} (xx nn ff - ), throws exception 0n<2160\leq n<2^{16} with parameter xx only if f=0f=0.

A.9.2. Catching and handling exceptions

  • F2FF\texttt{F2FF}TRY\texttt{TRY} (cc cc' - ), sets c2\texttt{c2} to cc', first saving the old value of c2\texttt{c2} both into the savelist of cc' and into the savelist of the current continuation, which is stored into cc.c0\texttt{c0} and cc'.c0\texttt{c0}. Then runs cc similarly to EXECUTE\texttt{EXECUTE}. If cc does not throw any exceptions, the original value of c2\texttt{c2} is automatically restored on return from cc. If an exception occurs, the execution is transferred to cc', but the original value of c2\texttt{c2} is restored in the process, so that cc' can re-throw the exception by THROWANY\texttt{THROWANY} if it cannot handle it by itself.
  • F3pr\texttt{F3}prTRYARGS\texttt{TRYARGS} pp,rr (cc cc' - ), similar to TRY\texttt{TRY}, but with CALLARGS\texttt{CALLARGS} pp,rr internally used instead of EXECUTE\texttt{EXECUTE}. In this way, all but the top 0p150\leq p\leq 15 stack elements will be saved into current continuation’s stack, and then restored upon return from either cc or cc', with the top 0r150\leq r\leq 15 values of the resulting stack of cc or cc' copied as return values.

A.10 Dictionary manipulation primitives

TVM’s dictionary support is discussed at length in 3.3. The basic operations with dictionaries are listed in 3.3.10, while the taxonomy of dictionary manipulation primitives is provided in 3.3.11. Here we use the concepts and notation introduced in those sections. Dictionaries admit two different representations as TVM stack values:
  • A Slice ss with a serialization of a TL-B value of type HashmapE(n,X)\mathit{HashmapE}(n,X). In other words, ss consists either of one bit equal to zero (if the dictionary is empty), or of one bit equal to one and a reference to a Cell containing the root of the binary tree, i.e., a serialized value of type Hashmap(n,X)\mathit{Hashmap}(n,X).
  • A “maybe Cellc?c^?, i.e., a value that is either a Cell (containing a serialized value of type Hashmap(n,X)\mathit{Hashmap}(n,X) as before) or a Null (corresponding to an empty dictionary). When a “maybe Cellc?c^? is used to represent a dictionary, we usually denote it by DD in the stack notation.
Most of the dictionary primitives listed below accept and return dictionaries in the second form, which is more convenient for stack manipulation. However, serialized dictionaries inside larger TL-B objects use the first representation. Opcodes starting with F4\texttt{F4} and F5\texttt{F5} are reserved for dictionary operations.

A.10.1. Dictionary creation

  • 6D\texttt{6D}NEWDICT\texttt{NEWDICT} ( - DD), returns a new empty dictionary. It is an alternative mnemonics for PUSHNULL\texttt{PUSHNULL}, cf. A.3.1.
  • 6E\texttt{6E}DICTEMPTY\texttt{DICTEMPTY} (DD - ??), checks whether dictionary DD is empty, and returns 1-1 or 00 accordingly. It is an alternative mnemonics for ISNULL\texttt{ISNULL}, cf. A.3.1.

A.10.2. Dictionary serialization and deserialization

  • CE\texttt{CE}STDICTS\texttt{STDICTS} (ss bb - bb'), stores a Slice-represented dictionary ss into Builder bb. It is actually a synonym for STSLICE\texttt{STSLICE}.
  • F400\texttt{F400}STDICT\texttt{STDICT} or STOPTREF\texttt{STOPTREF} (DD bb - bb'), stores dictionary DD into Builder bb, returning the resulting Builder bb'. In other words, if DD is a cell, performs STONE\texttt{STONE} and STREF\texttt{STREF}; if DD is Null, performs NIP\texttt{NIP} and STZERO\texttt{STZERO}; otherwise throws a type checking exception.
  • F401\texttt{F401}SKIPDICT\texttt{SKIPDICT} or SKIPOPTREF\texttt{SKIPOPTREF} (ss - ss'), equivalent to LDDICT\texttt{LDDICT}; NIP\texttt{NIP}.
  • F402\texttt{F402}LDDICTS\texttt{LDDICTS} (ss - ss' ss''), loads (parses) a (Slice-represented) dictionary ss' from Slice ss, and returns the remainder of ss as ss''. This is a “split function” for all HashmapE(n,X)\mathit{HashmapE}(n,X) dictionary types.
  • F403\texttt{F403}PLDDICTS\texttt{PLDDICTS} (ss - ss'), preloads a (Slice-represented) dictionary ss' from Slice ss. Approximately equivalent to LDDICTS\texttt{LDDICTS}; DROP\texttt{DROP}.
  • F404\texttt{F404}LDDICT\texttt{LDDICT} or LDOPTREF\texttt{LDOPTREF} (ss - DD ss'), loads (parses) a dictionary DD from Slice ss, and returns the remainder of ss as ss'. May be applied to dictionaries or to values of arbitrary (^Y(\text{\textasciicircum}Y) types.
  • F405\texttt{F405}PLDDICT\texttt{PLDDICT} or PLDOPTREF\texttt{PLDOPTREF} (ss - DD), preloads a dictionary DD from Slice ss. Approximately equivalent to LDDICT\texttt{LDDICT}; DROP\texttt{DROP}.
  • F406\texttt{F406}LDDICTQ\texttt{LDDICTQ} (ss - DD ss' 1-1 or ss 00), a quiet version of LDDICT\texttt{LDDICT}.
  • F407\texttt{F407}PLDDICTQ\texttt{PLDDICTQ} (ss - DD 1-1 or 00), a quiet version of PLDDICT\texttt{PLDDICT}.

A.10.3. Get\texttt{Get} dictionary operations

  • F40A\texttt{F40A}DICTGET\texttt{DICTGET} (kk DD nn - xx 1-1 or 00), looks up key kk (represented by a Slice, the first 0n10230\leq n\leq 1023 data bits of which are used as a key) in dictionary DD of type HashmapE(n,X)\mathit{HashmapE}(n,X) with nn-bit keys. On success, returns the value found as a Slice xx.
  • F40B\texttt{F40B}DICTGETREF\texttt{DICTGETREF} (kk DD nn - cc 1-1 or 00), similar to DICTGET\texttt{DICTGET}, but with a LDREF\texttt{LDREF}; ENDS\texttt{ENDS} applied to xx on success. This operation is useful for dictionaries of type HashmapE(n,^Y\mathit{HashmapE}(n, \text{\textasciicircum}Y).
  • F40C\texttt{F40C}DICTIGET\texttt{DICTIGET} (ii DD nn - xx 1-1 or 00), similar to DICTGET\texttt{DICTGET}, but with a signed (big-endian) nn-bit Integer ii as a key. If ii does not fit into nn bits, returns 00. If ii is a NaN\texttt{NaN}, throws an integer overflow exception.
  • F40D\texttt{F40D}DICTIGETREF\texttt{DICTIGETREF} (ii DD nn - cc 1-1 or 00), combines DICTIGET\texttt{DICTIGET} with DICTGETREF\texttt{DICTGETREF}: it uses signed nn-bit Integer ii as a key and returns a Cell instead of a Slice on success.
  • F40E\texttt{F40E}DICTUGET\texttt{DICTUGET} (ii DD nn - xx 1-1 or 00), similar to DICTIGET\texttt{DICTIGET}, but with unsigned (big-endian) nn-bit Integer ii used as a key.
  • F40F\texttt{F40F}DICTUGETREF\texttt{DICTUGETREF} (ii DD nn - cc 1-1 or 00), similar to DICTIGETREF\texttt{DICTIGETREF}, but with an unsigned nn-bit Integer key ii.

A.10.4. Set\texttt{Set}/Replace\texttt{Replace}/Add\texttt{Add} dictionary operations

The mnemonics of the following dictionary primitives are constructed in a systematic fashion according to the regular expression DICT[,I,U](SET,REPLACE,ADD)[GET][REF]\texttt{DICT}[,\texttt{I},\texttt{U}](\texttt{SET},\texttt{REPLACE},\texttt{ADD})[\texttt{GET}][\texttt{REF}] depending on the type of the key used (a Slice or a signed or unsigned Integer), the dictionary operation to be performed, and the way the values are accepted and returned (as Cells or as Slices). Therefore, we provide a detailed description only for some primitives, assuming that this information is sufficient for the reader to understand the precise action of the remaining primitives.
  • F412\texttt{F412}DICTSET\texttt{DICTSET} (xx kk DD nn - DD'), sets the value associated with nn-bit key kk (represented by a Slice as in DICTGET\texttt{DICTGET}) in dictionary DD (also represented by a Slice) to value xx (again a Slice), and returns the resulting dictionary as DD'.
  • F413\texttt{F413}DICTSETREF\texttt{DICTSETREF} (cc kk DD nn - DD'), similar to DICTSET\texttt{DICTSET}, but with the value set to a reference to Cell cc.
  • F414\texttt{F414}DICTISET\texttt{DICTISET} (xx ii DD nn - DD'), similar to DICTSET\texttt{DICTSET}, but with the key represented by a (big-endian) signed nn-bit integer ii. If ii does not fit into nn bits, a range check exception is generated.
  • F415\texttt{F415}DICTISETREF\texttt{DICTISETREF} (cc ii DD nn - DD'), similar to DICTSETREF\texttt{DICTSETREF}, but with the key a signed nn-bit integer as in DICTISET\texttt{DICTISET}.
  • F416\texttt{F416}DICTUSET\texttt{DICTUSET} (xx ii DD nn - DD'), similar to DICTISET\texttt{DICTISET}, but with ii an unsigned nn-bit integer.
  • F417\texttt{F417}DICTUSETREF\texttt{DICTUSETREF} (cc ii DD nn - DD'), similar to DICTISETREF\texttt{DICTISETREF}, but with ii unsigned.
  • F41A\texttt{F41A}DICTSETGET\texttt{DICTSETGET} (xx kk DD nn - DD' yy 1-1 or DD' 00), combines DICTSET\texttt{DICTSET} with DICTGET\texttt{DICTGET}: it sets the value corresponding to key kk to xx, but also returns the old value yy associated with the key in question, if present.
  • F41B\texttt{F41B}DICTSETGETREF\texttt{DICTSETGETREF} (cc kk DD nn - DD' cc' 1-1 or DD' 00), combines DICTSETREF\texttt{DICTSETREF} with DICTGETREF\texttt{DICTGETREF} similarly to DICTSETGET\texttt{DICTSETGET}.
  • F41C\texttt{F41C}DICTISETGET\texttt{DICTISETGET} (xx ii DD nn - DD' yy 1-1 or DD' 00), similar to DICTSETGET\texttt{DICTSETGET}, but with the key represented by a big-endian signed nn-bit Integer ii.
  • F41D\texttt{F41D}DICTISETGETREF\texttt{DICTISETGETREF} (cc ii DD nn - DD' cc' 1-1 or DD' 00), a version of DICTSETGETREF\texttt{DICTSETGETREF} with signed Integer ii as a key.
  • F41E\texttt{F41E}DICTUSETGET\texttt{DICTUSETGET} (xx ii DD nn - DD' yy 1-1 or DD' 00), similar to DICTISETGET\texttt{DICTISETGET}, but with ii an unsigned nn-bit integer.
  • F41F\texttt{F41F}DICTUSETGETREF\texttt{DICTUSETGETREF} (cc ii DD nn - DD' cc' 1-1 or DD' 00).
  • F422\texttt{F422}DICTREPLACE\texttt{DICTREPLACE} (xx kk DD nn - DD' 1-1 or DD 00), a Replace\texttt{Replace} operation, which is similar to DICTSET\texttt{DICTSET}, but sets the value of key kk in dictionary DD to xx only if the key kk was already present in DD.
  • F423\texttt{F423}DICTREPLACEREF\texttt{DICTREPLACEREF} (cc kk DD nn - DD' 1-1 or DD 00), a Replace\texttt{Replace} counterpart of DICTSETREF\texttt{DICTSETREF}.
  • F424\texttt{F424}DICTIREPLACE\texttt{DICTIREPLACE} (xx ii DD nn - DD' 1-1 or DD 00), a version of DICTREPLACE\texttt{DICTREPLACE} with signed nn-bit Integer ii used as a key.
  • F425\texttt{F425}DICTIREPLACEREF\texttt{DICTIREPLACEREF} (cc ii DD nn - DD' 1-1 or DD 00).
  • F426\texttt{F426}DICTUREPLACE\texttt{DICTUREPLACE} (xx ii DD nn - DD' 1-1 or DD 00).
  • F427\texttt{F427}DICTUREPLACEREF\texttt{DICTUREPLACEREF} (cc ii DD nn - DD' 1-1 or DD 00).
  • F42A\texttt{F42A}DICTREPLACEGET\texttt{DICTREPLACEGET} (xx kk DD nn - DD' yy 1-1 or DD 00), a Replace\texttt{Replace} counterpart of DICTSETGET\texttt{DICTSETGET}: on success, also returns the old value associated with the key in question.
  • F42B\texttt{F42B}DICTREPLACEGETREF\texttt{DICTREPLACEGETREF} (cc kk DD nn - DD' cc' 1-1 or DD 00).
  • F42C\texttt{F42C}DICTIREPLACEGET\texttt{DICTIREPLACEGET} (xx ii DD nn - DD' yy 1-1 or DD 00).
  • F42D\texttt{F42D}DICTIREPLACEGETREF\texttt{DICTIREPLACEGETREF} (cc ii DD nn - DD' cc' 1-1 or DD 00).
  • F42E\texttt{F42E}DICTUREPLACEGET\texttt{DICTUREPLACEGET} (xx ii DD nn - DD' yy 1-1 or DD 00).
  • F42F\texttt{F42F}DICTUREPLACEGETREF\texttt{DICTUREPLACEGETREF} (cc ii DD nn - DD' cc' 1-1 or DD 00).
  • F432\texttt{F432}DICTADD\texttt{DICTADD} (xx kk DD nn - DD' 1-1 or DD 00), an Add counterpart of DICTSET\texttt{DICTSET}: sets the value associated with key kk in dictionary DD to xx, but only if it is not already present in DD.
  • F433\texttt{F433}DICTADDREF\texttt{DICTADDREF} (cc kk DD nn - DD' 1-1 or DD 00).
  • F434\texttt{F434}DICTIADD\texttt{DICTIADD} (xx ii DD nn - DD' 1-1 or DD 00).
  • F435\texttt{F435}DICTIADDREF\texttt{DICTIADDREF} (cc ii DD nn - DD' 1-1 or DD 00).
  • F436\texttt{F436}DICTUADD\texttt{DICTUADD} (xx ii DD nn - DD' 1-1 or DD 00).
  • F437\texttt{F437}DICTUADDREF\texttt{DICTUADDREF} (cc ii DD nn - DD' 1-1 or DD 00).
  • F43A\texttt{F43A}DICTADDGET\texttt{DICTADDGET} (xx kk DD nn - DD' 1-1 or DD yy 00), an Add counterpart of DICTSETGET\texttt{DICTSETGET}: sets the value associated with key kk in dictionary DD to xx, but only if key kk is not already present in DD. Otherwise, just returns the old value yy without changing the dictionary.
  • F43B\texttt{F43B}DICTADDGETREF\texttt{DICTADDGETREF} (cc kk DD nn - DD' 1-1 or DD cc' 00), an Add counterpart of DICTSETGETREF\texttt{DICTSETGETREF}.
  • F43C\texttt{F43C}DICTIADDGET\texttt{DICTIADDGET} (xx ii DD nn - DD' 1-1 or DD yy 00).
  • F43D\texttt{F43D}DICTIADDGETREF\texttt{DICTIADDGETREF} (cc ii DD nn - DD' 1-1 or DD cc' 00).
  • F43E\texttt{F43E}DICTUADDGET\texttt{DICTUADDGET} (xx ii DD nn - DD' 1-1 or DD yy 00).
  • F43F\texttt{F43F}DICTUADDGETREF\texttt{DICTUADDGETREF} (cc ii DD nn - DD' 1-1 or DD cc' 00).

A.10.5. Builder-accepting variants of Set\texttt{Set} dictionary operations

The following primitives accept the new value as a Builder bb instead of a Slice xx, which often is more convenient if the value needs to be serialized from several components computed in the stack. (This is reflected by appending a B\texttt{B} to the mnemonics of the corresponding Set\texttt{Set} primitives that work with Slices.) The net effect is roughly equivalent to converting bb into a Slice by ENDC\texttt{ENDC}; CTOS\texttt{CTOS} and executing the corresponding primitive listed in A.10.4.
  • F441\texttt{F441}DICTSETB\texttt{DICTSETB} (bb kk DD nn - DD').
  • F442\texttt{F442}DICTISETB\texttt{DICTISETB} (bb ii DD nn - DD').
  • F443\texttt{F443}DICTUSETB\texttt{DICTUSETB} (bb ii DD nn - DD').
  • F445\texttt{F445}DICTSETGETB\texttt{DICTSETGETB} (bb kk DD nn - DD' yy 1-1 or DD' 00).
  • F446\texttt{F446}DICTISETGETB\texttt{DICTISETGETB} (bb ii DD nn - DD' yy 1-1 or DD' 00).
  • F447\texttt{F447}DICTUSETGETB\texttt{DICTUSETGETB} (bb ii DD nn - DD' yy 1-1 or DD' 00).
  • F449\texttt{F449}DICTREPLACEB\texttt{DICTREPLACEB} (bb kk DD nn - DD' 1-1 or DD 00).
  • F44A\texttt{F44A}DICTIREPLACEB\texttt{DICTIREPLACEB} (bb ii DD nn - DD' 1-1 or DD 00).
  • F44B\texttt{F44B}DICTUREPLACEB\texttt{DICTUREPLACEB} (bb ii DD nn - DD' 1-1 or DD 00).
  • F44D\texttt{F44D}DICTREPLACEGETB\texttt{DICTREPLACEGETB} (bb kk DD nn - DD' yy 1-1 or DD 00).
  • F44E\texttt{F44E}DICTIREPLACEGETB\texttt{DICTIREPLACEGETB} (bb ii DD nn - DD' yy 1-1 or DD 00).
  • F44F\texttt{F44F}DICTUREPLACEGETB\texttt{DICTUREPLACEGETB} (bb ii DD nn - DD' yy 1-1 or DD 00).
  • F451\texttt{F451}DICTADDB\texttt{DICTADDB} (bb kk DD nn - DD' 1-1 or DD 00).
  • F452\texttt{F452}DICTIADDB\texttt{DICTIADDB} (bb ii DD nn - DD' 1-1 or DD 00).
  • F453\texttt{F453}DICTUADDB\texttt{DICTUADDB} (bb ii DD nn - DD' 1-1 or DD 00).
  • F455\texttt{F455}DICTADDGETB\texttt{DICTADDGETB} (bb kk DD nn - DD' 1-1 or DD yy 00).
  • F456\texttt{F456}DICTIADDGETB\texttt{DICTIADDGETB} (bb ii DD nn - DD' 1-1 or DD yy 00).
  • F457\texttt{F457}DICTUADDGETB\texttt{DICTUADDGETB} (bb ii DD nn - DD' 1-1 or DD yy 00).

A.10.6. Delete\texttt{Delete} dictionary operations

  • F459\texttt{F459}DICTDEL\texttt{DICTDEL} (kk DD nn - DD' 1-1 or DD 00), deletes nn-bit key, represented by a Slice kk, from dictionary DD. If the key is present, returns the modified dictionary DD' and the success flag 1-1. Otherwise, returns the original dictionary DD and 00.
  • F45A\texttt{F45A}DICTIDEL\texttt{DICTIDEL} (ii DD nn - DD' ??), a version of DICTDEL\texttt{DICTDEL} with the key represented by a signed nn-bit Integer ii. If ii does not fit into nn bits, simply returns DD 00 (“key not found, dictionary unmodified”).
  • F45B\texttt{F45B}DICTUDEL\texttt{DICTUDEL} (ii DD nn - DD' ??), similar to DICTIDEL\texttt{DICTIDEL}, but with ii an unsigned nn-bit integer.
  • F462\texttt{F462}DICTDELGET\texttt{DICTDELGET} (kk DD nn - DD' xx 1-1 or DD 00), deletes nn-bit key, represented by a Slice kk, from dictionary DD. If the key is present, returns the modified dictionary DD', the original value xx associated with the key kk (represented by a Slice), and the success flag 1-1. Otherwise, returns the original dictionary DD and 00.
  • F463\texttt{F463}DICTDELGETREF\texttt{DICTDELGETREF} (kk DD nn - DD' cc 1-1 or DD 00), similar to DICTDELGET\texttt{DICTDELGET}, but with LDREF\texttt{LDREF}; ENDS\texttt{ENDS} applied to xx on success, so that the value returned cc is a Cell.
  • F464\texttt{F464}DICTIDELGET\texttt{DICTIDELGET} (ii DD nn - DD' xx 1-1 or DD 00), a variant of primitive DICTDELGET\texttt{DICTDELGET} with signed nn-bit integer ii as a key.
  • F465\texttt{F465}DICTIDELGETREF\texttt{DICTIDELGETREF} (ii DD nn - DD' cc 1-1 or DD 00), a variant of primitive DICTIDELGET\texttt{DICTIDELGET} returning a Cell instead of a Slice.
  • F466\texttt{F466}DICTUDELGET\texttt{DICTUDELGET} (ii DD nn - DD' xx 1-1 or DD 00), a variant of primitive DICTDELGET\texttt{DICTDELGET} with unsigned nn-bit integer ii as a key.
  • F467\texttt{F467}DICTUDELGETREF\texttt{DICTUDELGETREF} (ii DD nn - DD' cc 1-1 or DD 00), a variant of primitive DICTUDELGET\texttt{DICTUDELGET} returning a Cell instead of a Slice.

A.10.7. “Maybe reference” dictionary operations

The following operations assume that a dictionary is used to store values c?c^? of type Cell?\mathit{Cell}^? (“Maybe Cell”), which can be used in particular to store dictionaries as values in other dictionaries. The representation is as follows: if c?c^? is a Cell, it is stored as a value with no data bits and exactly one reference to this Cell. If c?c^? is Null, then the corresponding key must be absent from the dictionary altogether.
  • F469\texttt{F469}DICTGETOPTREF\texttt{DICTGETOPTREF} (kk DD nn - c?c^?), a variant of DICTGETREF\texttt{DICTGETREF} that returns Null instead of the value c?c^? if the key kk is absent from dictionary DD.
  • F46A\texttt{F46A}DICTIGETOPTREF\texttt{DICTIGETOPTREF} (ii DD nn - c?c^?), similar to DICTGETOPTREF\texttt{DICTGETOPTREF}, but with the key given by signed nn-bit Integer ii. If the key ii is out of range, also returns Null.
  • F46B\texttt{F46B}DICTUGETOPTREF\texttt{DICTUGETOPTREF} (ii DD nn - c?c^?), similar to DICTGETOPTREF\texttt{DICTGETOPTREF}, but with the key given by unsigned nn-bit Integer ii.
  • F46D\texttt{F46D}DICTSETGETOPTREF\texttt{DICTSETGETOPTREF} (c?c^? kk DD nn - DD' c~?\tilde{c}^?), a variant of both DICTGETOPTREF\texttt{DICTGETOPTREF} and DICTSETGETREF\texttt{DICTSETGETREF} that sets the value corresponding to key kk in dictionary DD to c?c^? (if c?c^? is Null, then the key is deleted instead), and returns the old value c~?\tilde{c}^? (if the key kk was absent before, returns Null instead).
  • F46E\texttt{F46E}DICTISETGETOPTREF\texttt{DICTISETGETOPTREF} (c?c^? ii DD nn - DD' c~?\tilde{c}^?), similar to primitive DICTSETGETOPTREF\texttt{DICTSETGETOPTREF}, but using signed nn-bit Integer ii as a key. If ii does not fit into nn bits, throws a range checking exception.
  • F46F\texttt{F46F}DICTUSETGETOPTREF\texttt{DICTUSETGETOPTREF} (c?c^? ii DD nn - DD' c~?\tilde{c}^?), similar to primitive DICTSETGETOPTREF\texttt{DICTSETGETOPTREF}, but using unsigned nn-bit Integer ii as a key.

A.10.8. Prefix code dictionary operations

These are some basic operations for constructing prefix code dictionaries (cf 3.4.2). The primary application for prefix code dictionaries is deserializing TL-B serialized data structures, or, more generally, parsing prefix codes. Therefore, most prefix code dictionaries will be constant and created at compile time, not by the following primitives. Some Get\texttt{Get} operations for prefix code dictionaries may be found in A.10.11. Other prefix code dictionary operations include:
  • F470\texttt{F470}PFXDICTSET\texttt{PFXDICTSET} (xx kk DD nn - DD' 1-1 or DD 00).
  • F471\texttt{F471}PFXDICTREPLACE\texttt{PFXDICTREPLACE} (xx kk DD nn - DD' 1-1 or DD 00).
  • F472\texttt{F472}PFXDICTADD\texttt{PFXDICTADD} (xx kk DD nn - DD' 1-1 or DD 00).
  • F473\texttt{F473}PFXDICTDEL\texttt{PFXDICTDEL} (kk DD nn - DD' 1-1 or DD 00).
These primitives are completely similar to their non-prefix code counterparts DICTSET\texttt{DICTSET} etc (cf. A.10.4), with the obvious difference that even a Set\texttt{Set} may fail in a prefix code dictionary, so a success flag must be returned by PFXDICTSET\texttt{PFXDICTSET} as well.

A.10.9. Variants of GetNext\texttt{GetNext} and GetPrev\texttt{GetPrev} operations

  • F474\texttt{F474}DICTGETNEXT\texttt{DICTGETNEXT} (kk DD nn - xx' kk' 1-1 or 00), computes the minimal key kk' in dictionary DD that is lexicographically greater than kk, and returns kk' (represented by a Slice) along with associated value xx' (also represented by a Slice).
  • F475\texttt{F475}DICTGETNEXTEQ\texttt{DICTGETNEXTEQ} (kk DD nn - xx' kk' 1-1 or 00), similar to DICTGETNEXT\texttt{DICTGETNEXT}, but computes the minimal key kk' that is lexicographically greater than or equal to kk.
  • F476\texttt{F476}DICTGETPREV\texttt{DICTGETPREV} (kk DD nn - xx' kk' 1-1 or 00), similar to DICTGETNEXT\texttt{DICTGETNEXT}, but computes the maximal key kk' lexicographically smaller than kk.
  • F477\texttt{F477}DICTGETPREVEQ\texttt{DICTGETPREVEQ} (kk DD nn - xx' kk' 1-1 or 00), similar to DICTGETPREV\texttt{DICTGETPREV}, but computes the maximal key kk' lexicographically smaller than or equal to kk.
  • F478\texttt{F478}DICTIGETNEXT\texttt{DICTIGETNEXT} (ii DD nn - xx' ii' 1-1 or 00), similar to DICTGETNEXT\texttt{DICTGETNEXT}, but interprets all keys in dictionary DD as big-endian signed nn-bit integers, and computes the minimal key ii' that is larger than Integer ii (which does not necessarily fit into nn bits).
  • F479\texttt{F479}DICTIGETNEXTEQ\texttt{DICTIGETNEXTEQ} (ii DD nn - xx' ii' 1-1 or 00).
  • F47A\texttt{F47A}DICTIGETPREV\texttt{DICTIGETPREV} (ii DD nn - xx' ii' 1-1 or 00).
  • F47B\texttt{F47B}DICTIGETPREVEQ\texttt{DICTIGETPREVEQ} (ii DD nn - xx' ii' 1-1 or 00).
  • F47C\texttt{F47C}DICTUGETNEXT\texttt{DICTUGETNEXT} (ii DD nn - xx' ii' 1-1 or 00), similar to DICTGETNEXT\texttt{DICTGETNEXT}, but interprets all keys in dictionary DD as big-endian unsigned nn-bit integers, and computes the minimal key ii' that is larger than Integer ii (which does not necessarily fit into nn bits, and is not necessarily non-negative).
  • F47D\texttt{F47D}DICTUGETNEXTEQ\texttt{DICTUGETNEXTEQ} (ii DD nn - xx' ii' 1-1 or 00).
  • F47E\texttt{F47E}DICTUGETPREV\texttt{DICTUGETPREV} (ii DD nn - xx' ii' 1-1 or 00).
  • F47F\texttt{F47F}DICTUGETPREVEQ\texttt{DICTUGETPREVEQ} (ii DD nn - xx' ii' 1-1 or 00).

A.10.10. GetMin\texttt{GetMin}, GetMax\texttt{GetMax}, RemoveMin\texttt{RemoveMin}, RemoveMax\texttt{RemoveMax} operations

  • F482\texttt{F482}DICTMIN\texttt{DICTMIN} (DD nn - xx kk 1-1 or 00), computes the minimal key kk (represented by a Slice with nn data bits) in dictionary DD, and returns kk along with the associated value xx.
  • F483\texttt{F483}DICTMINREF\texttt{DICTMINREF} (DD nn - cc kk 1-1 or 00), similar to DICTMIN\texttt{DICTMIN}, but returns the only reference in the value as a Cell cc.
  • F484\texttt{F484}DICTIMIN\texttt{DICTIMIN} (DD nn - xx ii 1-1 or 00), somewhat similar to DICTMIN\texttt{DICTMIN}, but computes the minimal key ii under the assumption that all keys are big-endian signed nn-bit integers. Notice that the key and value returned may differ from those computed by DICTMIN\texttt{DICTMIN} and DICTUMIN\texttt{DICTUMIN}.
  • F485\texttt{F485}DICTIMINREF\texttt{DICTIMINREF} (DD nn - cc ii 1-1 or 00).
  • F486\texttt{F486}DICTUMIN\texttt{DICTUMIN} (DD nn - xx ii 1-1 or 00), similar to DICTMIN\texttt{DICTMIN}, but returns the key as an unsigned nn-bit Integer ii.
  • F487\texttt{F487}DICTUMINREF\texttt{DICTUMINREF} (DD nn - cc ii 1-1 or 00).
  • F48A\texttt{F48A}DICTMAX\texttt{DICTMAX} (DD nn - xx kk 1-1 or 00), computes the maximal key kk (represented by a Slice with nn data bits) in dictionary DD, and returns kk along with the associated value xx.
  • F48B\texttt{F48B}DICTMAXREF\texttt{DICTMAXREF} (DD nn - cc kk 1-1 or 00).
  • F48C\texttt{F48C}DICTIMAX\texttt{DICTIMAX} (DD nn - xx ii 1-1 or 00).
  • F48D\texttt{F48D}DICTIMAXREF\texttt{DICTIMAXREF} (DD nn - cc ii 1-1 or 00).
  • F48E\texttt{F48E}DICTUMAX\texttt{DICTUMAX} (DD nn - xx ii 1-1 or 00).
  • F48F\texttt{F48F}DICTUMAXREF\texttt{DICTUMAXREF} (DD nn - cc ii 1-1 or 00).
  • F492\texttt{F492}DICTREMMIN\texttt{DICTREMMIN} (DD nn - DD' xx kk 1-1 or DD 00), computes the minimal key kk (represented by a Slice with nn data bits) in dictionary DD, removes kk from the dictionary, and returns kk along with the associated value xx and the modified dictionary DD'.
  • F493\texttt{F493}DICTREMMINREF\texttt{DICTREMMINREF} (DD nn - DD' cc kk 1-1 or DD 00), similar to DICTREMMIN\texttt{DICTREMMIN}, but returns the only reference in the value as a Cell cc.
  • F494\texttt{F494}DICTIREMMIN\texttt{DICTIREMMIN} (DD nn - DD' xx ii 1-1 or DD 00), somewhat similar to DICTREMMIN\texttt{DICTREMMIN}, but computes the minimal key ii under the assumption that all keys are big-endian signed nn-bit integers. Notice that the key and value returned may differ from those computed by DICTREMMIN\texttt{DICTREMMIN} and DICTUREMMIN\texttt{DICTUREMMIN}.
  • F495\texttt{F495}DICTIREMMINREF\texttt{DICTIREMMINREF} (DD nn - DD' cc ii 1-1 or DD 00).
  • F496\texttt{F496}DICTUREMMIN\texttt{DICTUREMMIN} (DD nn - DD' xx ii 1-1 or DD 00), similar to DICTREMMIN\texttt{DICTREMMIN}, but returns the key as an unsigned nn-bit Integer ii.
  • F497\texttt{F497}DICTUREMMINREF\texttt{DICTUREMMINREF} (DD nn - DD' cc ii 1-1 or DD 00).
  • F49A\texttt{F49A}DICTREMMAX\texttt{DICTREMMAX} (DD nn - DD' xx kk 1-1 or DD 00), computes the maximal key kk (represented by a Slice with nn data bits) in dictionary DD, removes kk from the dictionary, and returns kk along with the associated value xx and the modified dictionary DD'.
  • F49B\texttt{F49B}DICTREMMAXREF\texttt{DICTREMMAXREF} (DD nn - DD' cc kk 1-1 or DD 00).
  • F49C\texttt{F49C}DICTIREMMAX\texttt{DICTIREMMAX} (DD nn - DD' xx ii 1-1 or DD 00).
  • F49D\texttt{F49D}DICTIREMMAXREF\texttt{DICTIREMMAXREF} (DD nn - DD' cc ii 1-1 or DD 00).
  • F49E\texttt{F49E}DICTUREMMAX\texttt{DICTUREMMAX} (DD nn - DD' xx ii 1-1 or DD 00).
  • F49F\texttt{F49F}DICTUREMMAXREF\texttt{DICTUREMMAXREF} (DD nn - DD' cc ii 1-1 or DD 00).

A.10.11. Special Get\texttt{Get} dictionary and prefix code dictionary operations, and constant dictionaries

  • F4A0\texttt{F4A0}DICTIGETJMP\texttt{DICTIGETJMP} (ii DD nn - ), similar to DICTIGET\texttt{DICTIGET} (cf. A.10.12), but with xx BLESS\texttt{BLESS}ed into a continuation with a subsequent JMPX\texttt{JMPX} to it on success. On failure, does nothing. This is useful for implementing switch\texttt{switch}/case\texttt{case} constructions.
  • F4A1\texttt{F4A1}DICTUGETJMP\texttt{DICTUGETJMP} (ii DD nn - ), similar to DICTIGETJMP\texttt{DICTIGETJMP}, but performs DICTUGET\texttt{DICTUGET} instead of DICTIGET\texttt{DICTIGET}.
  • F4A2\texttt{F4A2}DICTIGETEXEC\texttt{DICTIGETEXEC} (ii DD nn - ), similar to DICTIGETJMP\texttt{DICTIGETJMP}, but with EXECUTE\texttt{EXECUTE} instead of JMPX\texttt{JMPX}.
  • F4A3\texttt{F4A3}DICTUGETEXEC\texttt{DICTUGETEXEC} (ii DD nn - ), similar to DICTUGETJMP\texttt{DICTUGETJMP}, but with EXECUTE\texttt{EXECUTE} instead of JMPX\texttt{JMPX}.
  • F4A6_n\texttt{F4A6\_}nDICTPUSHCONST\texttt{DICTPUSHCONST} nn ( - DD nn), pushes a non-empty constant dictionary DD (as a Cell?\mathit{Cell}^?) along with its key length 0n10230\leq n\leq 1023, stored as a part of the instruction. The dictionary itself is created from the first of remaining references of the current continuation. In this way, the complete DICTPUSHCONST\texttt{DICTPUSHCONST} instruction can be obtained by first serializing xF4A8_\texttt{xF4A8\_}, then the non-empty dictionary itself (one 1\texttt{1} bit and a cell reference), and then the unsigned 10-bit integer nn (as if by a STU 10\texttt{STU 10} instruction). An empty dictionary can be pushed by a NEWDICT\texttt{NEWDICT} primitive (cf. A.10.1) instead.
  • F4A8\texttt{F4A8}PFXDICTGETQ\texttt{PFXDICTGETQ} (ss DD nn - ss' xx ss'' 1-1 or ss 00), looks up the unique prefix of Slice ss present in the prefix code dictionary represented by Cell?\mathit{Cell}^? DD and 0n10230\leq n\leq 1023. If found, the prefix of ss is returned as ss', and the corresponding value (also a Slice) as xx. The remainder of ss is returned as a Slice ss''. If no prefix of ss is a key in prefix code dictionary DD, returns the unchanged ss and a zero flag to indicate failure.
  • F4A9\texttt{F4A9}PFXDICTGET\texttt{PFXDICTGET} (ss DD nn - ss' xx ss''), similar to PFXDICTGETQ\texttt{PFXDICTGETQ}, but throws a cell deserialization failure exception on failure.
  • F4AA\texttt{F4AA}PFXDICTGETJMP\texttt{PFXDICTGETJMP} (ss DD nn - ss' ss'' or ss), similar to PFXDICTGETQ\texttt{PFXDICTGETQ}, but on success BLESS\texttt{BLESS}es the value xx into a Continuation and transfers control to it as if by a JMPX\texttt{JMPX}. On failure, returns ss unchanged and continues execution.
  • F4AB\texttt{F4AB}PFXDICTGETEXEC\texttt{PFXDICTGETEXEC} (ss DD nn - ss' ss''), similar to PFXDICTGETJMP\texttt{PFXDICTGETJMP}, but EXEC\texttt{EXEC}utes the continuation found instead of jumping to it. On failure, throws a cell deserialization exception.
  • F4AE_n\texttt{F4AE\_}nPFXDICTCONSTGETJMP\texttt{PFXDICTCONSTGETJMP} nn or PFXDICTSWITCH\texttt{PFXDICTSWITCH} nn (ss - ss' ss'' or ss), combines DICTPUSHCONST\texttt{DICTPUSHCONST} nn for 0n10230\leq n\leq 1023 with PFXDICTGETJMP\texttt{PFXDICTGETJMP}.
  • F4BC\texttt{F4BC}DICTIGETJMPZ\texttt{DICTIGETJMPZ} (ii DD nn - ii or nothing), a variant of DICTIGETJMP\texttt{DICTIGETJMP} that returns index ii on failure.
  • F4BD\texttt{F4BD}DICTUGETJMPZ\texttt{DICTUGETJMPZ} (ii DD nn - ii or nothing), a variant of DICTUGETJMP\texttt{DICTUGETJMP} that returns index ii on failure.
  • F4BE\texttt{F4BE}DICTIGETEXECZ\texttt{DICTIGETEXECZ} (ii DD nn - ii or nothing), a variant of DICTIGETEXEC\texttt{DICTIGETEXEC} that returns index ii on failure.
  • F4BF\texttt{F4BF}DICTUGETEXECZ\texttt{DICTUGETEXECZ} (ii DD nn - ii or nothing), a variant of DICTUGETEXEC\texttt{DICTUGETEXEC} that returns index ii on failure.

A.10.12. SubDict\texttt{SubDict} dictionary operations

  • F4B1\texttt{F4B1}SUBDICTGET\texttt{SUBDICTGET} (kk ll DD nn - DD'), constructs a subdictionary consisting of all keys beginning with prefix kk (represented by a Slice, the first 0ln10230\leq l\leq n\leq 1023 data bits of which are used as a key) of length ll in dictionary DD of type HashmapE(n,X)\mathit{HashmapE}(n,X) with nn-bit keys. On success, returns the new subdictionary of the same type HashmapE(n,X)\mathit{HashmapE}(n,X) as a Slice DD'.
  • F4B2\texttt{F4B2}SUBDICTIGET\texttt{SUBDICTIGET} (xx ll DD nn - DD'), variant of SUBDICTGET\texttt{SUBDICTGET} with the prefix represented by a signed big-endian ll-bit Integer xx, where necessarily l257l\leq257.
  • F4B3\texttt{F4B3}SUBDICTUGET\texttt{SUBDICTUGET} (xx ll DD nn - DD'), variant of SUBDICTGET\texttt{SUBDICTGET} with the prefix represented by an unsigned big-endian ll-bit Integer xx, where necessarily l256l\leq256.
  • F4B5\texttt{F4B5}SUBDICTRPGET\texttt{SUBDICTRPGET} (kk ll DD nn - DD'), similar to SUBDICTGET\texttt{SUBDICTGET}, but removes the common prefix kk from all keys of the new dictionary DD', which becomes of type HashmapE(nl,X)\mathit{HashmapE}(n-l,X).
  • F4B6\texttt{F4B6}SUBDICTIRPGET\texttt{SUBDICTIRPGET} (xx ll DD nn - DD'), variant of SUBDICTRPGET\texttt{SUBDICTRPGET} with the prefix represented by a signed big-endian ll-bit Integer xx, where necessarily l257l\leq257.
  • F4B7\texttt{F4B7}SUBDICTURPGET\texttt{SUBDICTURPGET} (xx ll DD nn - DD'), variant of SUBDICTRPGET\texttt{SUBDICTRPGET} with the prefix represented by an unsigned big-endian ll-bit Integer xx, where necessarily l256l\leq256.
  • F4BC\texttt{F4BC}F4BF\texttt{F4BF} — used by DICTZ\texttt{DICT\ldots Z} primitives in A.10.11.

A.11 Application-specific primitives

Opcode range F8\texttt{F8}FB\texttt{FB} is reserved for the application-specific primitives. When TVM is used to execute TON Blockchain smart contracts, these application-specific primitives are in fact TON Blockchain-specific.

A.11.1. External actions and access to blockchain configuration data

Some of the primitives listed below pretend to produce some externally visible actions, such as sending a message to another smart contract. In fact, the execution of a smart contract in TVM never has any effect apart from a modification of the TVM state. All external actions are collected into a linked list stored in special register c5\texttt{c5} (“output actions”). Additionally, some primitives use the data kept in the first component of the Tuple stored in c7\texttt{c7} (“root of temporary data” cf 1.3.2). Smart contracts are free to modify any other data kept in the cell c7\texttt{c7}, provided the first reference remains intact (otherwise some application-specific primitives would be likely to throw exceptions when invoked). Most of the primitives listed below use 16-bit opcodes. Of the following primitives, only the first two are “pure” in the sense that they do not use c5\texttt{c5} or c7\texttt{c7}.
  • F800\texttt{F800}ACCEPT\texttt{ACCEPT}, sets current gas limit glg_l to its maximal allowed value gmg_m, and resets the gas credit gcg_c to zero (cf. 1.4), decreasing the value of grg_r by gcg_c in the process. In other words, the current smart contract agrees to buy some gas to finish the current transaction. This action is required to process external messages, which bring no value (hence no gas) with themselves.
  • F801\texttt{F801}SETGASLIMIT\texttt{SETGASLIMIT} (gg - ), sets current gas limit glg_l to the minimum of gg and gmg_m, and resets the gas credit gcg_c to zero. If the gas consumed so far (including the present instruction) exceeds the resulting value of glg_l, an (unhandled) out of gas exception is thrown before setting new gas limits. Notice that SETGASLIMIT\texttt{SETGASLIMIT} with an argument g2631g\geq 2^{63}-1 is equivalent to ACCEPT\texttt{ACCEPT}.
  • F802\texttt{F802}BUYGAS\texttt{BUYGAS} (xx - ), computes the amount of gas that can be bought for xx nanograms, and sets glg_l accordingly in the same way as SETGASLIMIT\texttt{SETGASLIMIT}.
  • F804\texttt{F804}GRAMTOGAS\texttt{GRAMTOGAS} (xx - gg), computes the amount of gas that can be bought for xx nanograms. If xx is negative, returns 0. If gg exceeds 26312^{63}-1, it is replaced with this value.
  • F805\texttt{F805}GASTOGRAM\texttt{GASTOGRAM} (gg - xx), computes the price of gg gas in nanograms.
  • F806\texttt{F806}F80E\texttt{F80E} — Reserved for gas-related primitives.
  • F80F\texttt{F80F}COMMIT\texttt{COMMIT} ( - ), commits the current state of registers c4\texttt{c4} (“persistent data”) and c5\texttt{c5} (“actions”) so that the current execution is considered “successful” with the saved values even if an exception is thrown later.

A.11.3. Pseudo-random number generator primitives

The pseudo-random number generator uses the random seed (parameter #6, cf. A.11.4), an unsigned 256-bit Integer, and (sometimes) other data kept in c7\texttt{c7}. The initial value of the random seed before a smart contract is executed in TON Blockchain is a hash of the smart contract address and the global block random seed. If there are several runs of the same smart contract inside a block, then all of these runs will have the same random seed. This can be fixed, for example, by running LTIME\texttt{LTIME}; ADDRAND\texttt{ADDRAND} before using the pseudo-random number generator for the first time.
  • F810\texttt{F810}RANDU256\texttt{RANDU256} ( - xx), generates a new pseudo-random unsigned 256-bit Integer xx. The algorithm is as follows: if rr is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its Sha512(r)\text{Sha512}(r) is computed; the first 32 bytes of this hash are stored as the new value rr' of the random seed, and the remaining 32 bytes are returned as the next random value xx.
  • F811\texttt{F811}RAND\texttt{RAND} (yy - zz), generates a new pseudo-random integer zz in the range 0y10\ldots y-1 (or y1y\ldots-1, if y<0y<0). More precisely, an unsigned random value xx is generated as in RAND256U\texttt{RAND256U}; then z:=xy/2256z:=\lfloor xy/2^{256}\rfloor is computed. Equivalent to RANDU256\texttt{RANDU256}; MULRSHIFT 256\texttt{MULRSHIFT 256}.
  • F814\texttt{F814}SETRAND\texttt{SETRAND} (xx - ), sets the random seed to unsigned 256-bit Integer xx.
  • F815\texttt{F815}ADDRAND\texttt{ADDRAND} (xx - ), mixes unsigned 256-bit Integer xx into the random seed rr by setting the random seed to SHA-256 of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed rr, and the second with the big-endian representation of xx.
  • F810\texttt{F810}F81F\texttt{F81F} — Reserved for pseudo-random number generator primitives.

A.11.4. Configuration primitives

The following primitives read configuration data provided in the Tuple stored in the first component of the Tuple at c7\texttt{c7}. Whenever TVM is invoked for executing TON Blockchain smart contracts, this Tuple is initialized by a SmartContractInfo structure; configuration primitives assume that it has remained intact.
  • F82i\texttt{F82}iGETPARAM\texttt{GETPARAM} ii ( - xx), returns the ii-th parameter from the Tuple provided at c7\texttt{c7} for 0i<160\leq i<16. Equivalent to PUSH c7\texttt{PUSH c7}; FIRST\texttt{FIRST}; INDEX\texttt{INDEX} ii. If one of these internal operations fails, throws an appropriate type checking or range checking exception.
  • F823\texttt{F823}NOW\texttt{NOW} ( - xx), returns the current Unix time as an Integer. If it is impossible to recover the requested value starting from c7\texttt{c7}, throws a type checking or range checking exception as appropriate. Equivalent to GETPARAM 3\texttt{GETPARAM 3}.
  • F824\texttt{F824}BLOCKLT\texttt{BLOCKLT} ( - xx), returns the starting logical time of the current block. Equivalent to GETPARAM 4\texttt{GETPARAM 4}.
  • F825\texttt{F825}LTIME\texttt{LTIME} ( - xx), returns the logical time of the current transaction. Equivalent to GETPARAM 5\texttt{GETPARAM 5}.
  • F826\texttt{F826}RANDSEED\texttt{RANDSEED} ( - xx), returns the current random seed as an unsigned 256-bit Integer. Equivalent to GETPARAM 6\texttt{GETPARAM 6}.
  • F827\texttt{F827}BALANCE\texttt{BALANCE} ( - tt), returns the remaining balance of the smart contract as a Tuple consisting of an Integer (the remaining Gram balance in nanograms) and a Maybe Cell (a dictionary with 32-bit keys representing the balance of “extra currencies”). Equivalent to GETPARAM 7\texttt{GETPARAM 7}. Note that RAW\texttt{RAW} primitives such as SENDRAWMSG\texttt{SENDRAWMSG} do not update this field.
  • F828\texttt{F828}MYADDR\texttt{MYADDR} ( - ss), returns the internal address of the current smart contract as a Slice with a MsgAddressInt\texttt{MsgAddressInt}. If necessary, it can be parsed further using primitives such as PARSESTDADDR\texttt{PARSESTDADDR} or REWRITESTDADDR\texttt{REWRITESTDADDR}. Equivalent to GETPARAM 8\texttt{GETPARAM 8}.
  • F829\texttt{F829}CONFIGROOT\texttt{CONFIGROOT} ( - DD), returns the Maybe Cell DD with the current global configuration dictionary. Equivalent to GETPARAM 9\texttt{GETPARAM 9}.
  • F830\texttt{F830}CONFIGDICT\texttt{CONFIGDICT} ( - DD 3232), returns the global configuration dictionary along with its key length (32). Equivalent to CONFIGROOT\texttt{CONFIGROOT}; PUSHINT 32\texttt{PUSHINT 32}.
  • F832\texttt{F832}CONFIGPARAM\texttt{CONFIGPARAM} (ii - cc 1-1 or 00), returns the value of the global configuration parameter with integer index ii as a Cell cc, and a flag to indicate success. Equivalent to CONFIGDICT\texttt{CONFIGDICT}; DICTIGETREF\texttt{DICTIGETREF}.
  • F833\texttt{F833}CONFIGOPTPARAM\texttt{CONFIGOPTPARAM} (ii - c?c^?), returns the value of the global configuration parameter with integer index ii as a Maybe Cell c?c^?. Equivalent to CONFIGDICT\texttt{CONFIGDICT}; DICTIGETOPTREF\texttt{DICTIGETOPTREF}.
  • F820\texttt{F820}-F83F\texttt{F83F} — Reserved for configuration primitives.

A.11.5. Global variable primitives

The “global variables” may be helpful in implementing some high-level smart-contract languages. They are in fact stored as components of the Tuple at c7\texttt{c7}: the kk-th global variable simply is the kk-th component of this Tuple, for 1k2541\leq k\leq 254. By convention, the 00-th component is used for the “configuration parameters” of A.11.4, so it is not available as a global variable.
  • F840\texttt{F840}GETGLOBVAR\texttt{GETGLOBVAR} (kk - xx), returns the kk-th global variable for 0k<2550\leq k<255. Equivalent to PUSH c7\texttt{PUSH c7}; SWAP\texttt{SWAP}; INDEXVARQ\texttt{INDEXVARQ} (cf. A.3.2).
  • F85_k\texttt{F85\_}kGETGLOB\texttt{GETGLOB} kk ( - xx), returns the kk-th global variable for 1k311\leq k\leq 31. Equivalent to PUSH c7\texttt{PUSH c7}; INDEXQ\texttt{INDEXQ} kk.
  • F860\texttt{F860}SETGLOBVAR\texttt{SETGLOBVAR} (xx kk - ), assigns xx to the kk-th global variable for 0k<2550\leq k<255. Equivalent to PUSH c7\texttt{PUSH c7}; ROTREV\texttt{ROTREV}; SETINDEXVARQ\texttt{SETINDEXVARQ}; POP c7\texttt{POP c7}.
  • F87_k\texttt{F87\_}kSETGLOB\texttt{SETGLOB} kk (xx - ), assigns xx to the kk-th global variable for 1k311\leq k\leq 31. Equivalent to PUSH c7\texttt{PUSH c7}; SWAP\texttt{SWAP}; SETINDEXQ\texttt{SETINDEXQ} kk; POP c7\texttt{POP c7}.

A.11.6. Hashing and cryptography primitives

  • F900\texttt{F900}HASHCU\texttt{HASHCU} (cc - xx), computes the representation hash (cf. 3.1.5) of a Cell cc and returns it as a 256-bit unsigned integer xx. Useful for signing and checking signatures of arbitrary entities represented by a tree of cells.
  • F901\texttt{F901}HASHSU\texttt{HASHSU} (ss - xx), computes the hash of a Slice ss and returns it as a 256-bit unsigned integer xx. The result is the same as if an ordinary cell containing only data and references from ss had been created and its hash computed by HASHCU\texttt{HASHCU}.
  • F902\texttt{F902}SHA256U\texttt{SHA256U} (ss - xx), computes SHA-256 of the data bits of Slice ss. If the bit length of ss is not divisible by eight, throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer xx.
  • F910\texttt{F910}CHKSIGNU\texttt{CHKSIGNU} (hh ss kk - ??), checks the Ed25519-signature ss of a hash hh (a 256-bit unsigned integer, usually computed as the hash of some data) using public key kk (also represented by a 256-bit unsigned integer). The signature ss must be a Slice containing at least 512 data bits; only the first 512 bits are used. The result is 1-1 if the signature is valid, 00 otherwise. Notice that CHKSIGNU\texttt{CHKSIGNU} is equivalent to ROT\texttt{ROT}; NEWB\texttt{NEWB}; STU 256\texttt{STU 256}; ENDB\texttt{ENDB}; NEWC\texttt{NEWC}; ROTREV\texttt{ROTREV}; CHKSIGNS\texttt{CHKSIGNS}, i.e., to CHKSIGNS\texttt{CHKSIGNS} with the first argument dd set to 256-bit Slice containing hh. Therefore, if hh is computed as the hash of some data, these data are hashed twice, the second hashing occurring inside CHKSIGNS\texttt{CHKSIGNS}.
  • F911\texttt{F911}CHKSIGNS\texttt{CHKSIGNS} (dd ss kk - ??), checks whether ss is a valid Ed25519-signature of the data portion of Slice dd using public key kk, similarly to CHKSIGNU\texttt{CHKSIGNU}. If the bit length of Slice dd is not divisible by eight, throws a cell underflow exception. The verification of Ed25519 signatures is the standard one, with SHA-256 used to reduce dd to the 256-bit number that is actually signed.
  • F912\texttt{F912}-F93F\texttt{F93F} — Reserved for hashing and cryptography primitives.

A.11.7. Miscellaneous primitives

  • F940\texttt{F940}CDATASIZEQ\texttt{CDATASIZEQ} (cc nn - xx yy zz 1-1 or 00), recursively computes the count of distinct cells xx, data bits yy, and cell references zz in the dag rooted at Cell cc, effectively returning the total storage used by this dag taking into account the identification of equal cells. The values of xx, yy, and zz are computed by a depth-first traversal of this dag, with a hash table of visited cell hashes used to prevent visits of already-visited cells. The total count of visited cells xx cannot exceed non-negative Integer nn; otherwise the computation is aborted before visiting the (n+1)(n+1)-st cell and a zero is returned to indicate failure. If cc is Null, returns x=y=z=0x=y=z=0.
  • F941\texttt{F941}CDATASIZE\texttt{CDATASIZE} (cc nn - xx yy zz), a non-quiet version of CDATASIZEQ\texttt{CDATASIZEQ} that throws a cell overflow exception (8) on failure.
  • F942\texttt{F942}SDATASIZEQ\texttt{SDATASIZEQ} (ss nn - xx yy zz 1-1 or 00), similar to CDATASIZEQ\texttt{CDATASIZEQ}, but accepting a Slice ss instead of a Cell. The returned value of xx does not take into account the cell that contains the slice ss itself; however, the data bits and the cell references of ss are accounted for in yy and zz.
  • F943\texttt{F943}SDATASIZE\texttt{SDATASIZE} (ss nn - xx yy zz), a non-quiet version of SDATASIZEQ\texttt{SDATASIZEQ} that throws a cell overflow exception (8) on failure.
  • F944\texttt{F944}-F97F\texttt{F97F} — Reserved for miscellaneous TON-specific primitives that do not fall into any other specific category.

A.11.8. Currency manipulation primitives

  • FA00\texttt{FA00}LDGRAMS\texttt{LDGRAMS} or LDVARUINT16\texttt{LDVARUINT16} (ss - xx ss'), loads (deserializes) a Gram\texttt{Gram} or VarUInteger 16\texttt{VarUInteger 16} amount from CellSlice ss, and returns the amount as Integer xx along with the remainder ss' of ss. The expected serialization of xx consists of a 4-bit unsigned big-endian integer ll, followed by an 8l8l-bit unsigned big-endian representation of xx. The net effect is approximately equivalent to LDU 4\texttt{LDU 4}; SWAP\texttt{SWAP}; LSHIFT 3\texttt{LSHIFT 3}; LDUX\texttt{LDUX}.
  • FA01\texttt{FA01}LDVARINT16\texttt{LDVARINT16} (ss - xx ss'), similar to LDVARUINT16\texttt{LDVARUINT16}, but loads a signed Integer xx. Approximately equivalent to LDU 4\texttt{LDU 4}; SWAP\texttt{SWAP}; LSHIFT 3\texttt{LSHIFT 3}; LDIX\texttt{LDIX}.
  • FA02\texttt{FA02}STGRAMS\texttt{STGRAMS} or STVARUINT16\texttt{STVARUINT16} (bb xx - bb'), stores (serializes) an Integer xx in the range 0212010\ldots2^{120}-1 into Builder bb, and returns the resulting Builder bb'. The serialization of xx consists of a 4-bit unsigned big-endian integer ll, which is the smallest integer l0l\geq0, such that x<28lx<2^{8l}, followed by an 8l8l-bit unsigned big-endian representation of xx. If xx does not belong to the supported range, a range check exception is thrown.
  • FA03\texttt{FA03}STVARINT16\texttt{STVARINT16} (bb xx - bb'), similar to STVARUINT16\texttt{STVARUINT16}, but serializes a signed Integer xx in the range 211921191-2^{119}\ldots2^{119}-1.
  • FA04\texttt{FA04}LDVARUINT32\texttt{LDVARUINT32} (ss - xx ss'), loads (deserializes) a VarUInteger 32\texttt{VarUInteger 32} from CellSlice ss, and returns the deserialized value as an Integer 0x<22480\leq x<2^{248}. The expected serialization of xx consists of a 5-bit unsigned big-endian integer ll, followed by an 8l8l-bit unsigned big-endian representation of xx. The net effect is approximately equivalent to LDU 5\texttt{LDU 5}; SWAP\texttt{SWAP}; SHIFT 3\texttt{SHIFT 3}; LDUX\texttt{LDUX}.
  • FA05\texttt{FA05}LDVARINT32\texttt{LDVARINT32} (ss - xx ss'), deserializes a VarInteger 32\texttt{VarInteger 32} from CellSlice ss, and returns the deserialized value as an Integer 2247x<2247-2^{247}\leq x<2^{247}.
  • FA06\texttt{FA06}STVARUINT32\texttt{STVARUINT32} (bb xx - bb'), serializes an Integer 0x<22480\leq x<2^{248} as a VarUInteger 32\texttt{VarUInteger 32}.
  • FA07\texttt{FA07}STVARINT32\texttt{STVARINT32} (bb xx - bb'), serializes an Integer 2247x<2247-2^{247}\leq x<2^{247} as a VarInteger 32\texttt{VarInteger 32}.
  • FA08\texttt{FA08}-FA1F\texttt{FA1F} — Reserved for currency manipulation primitives.

A.11.9. Message and address manipulation primitives

The message and address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme (cf. 3.3.4):
addr_none$00 = MsgAddressExt;
addr_extern$01 len:(## 9) external_address:(bits len) 
             = MsgAddressExt;
anycast_info$_ depth:(#<= 30) { depth >= 1 }
   rewrite_pfx:(bits depth) = Anycast;
addr_std$10 anycast:(Maybe Anycast) 
   workchain_id:int8 address:bits256  = MsgAddressInt;
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) 
   workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
_ _:MsgAddressInt = MsgAddress;
_ _:MsgAddressExt = MsgAddress;

int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
  src:MsgAddress dest:MsgAddressInt 
  value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
  created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
  created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
A deserialized MsgAddress\texttt{MsgAddress} is represented by a Tuple tt as follows:
  • addr_none\texttt{addr\_none} is represented by t=(0)t=(0), i.e., a Tuple containing exactly one Integer equal to zero.
  • addr_extern\texttt{addr\_extern} is represented by t=(1,s)t=(1,s), where Slice ss contains the field external_address\texttt{external\_address}. In other words, tt is a pair (a Tuple consisting of two entries), containing an Integer equal to one and Slice ss.
  • addr_std\texttt{addr\_std} is represented by t=(2,u,x,s)t=(2,u,x,s), where uu is either a Null (if anycast\texttt{anycast} is absent) or a Slice ss' containing rewrite_pfx\texttt{rewrite\_pfx} (if anycast\texttt{anycast} is present). Next, Integer xx is the workchain_id\texttt{workchain\_id}, and Slice ss contains the address\texttt{address}.
  • addr_var\texttt{addr\_var} is represented by t=(3,u,x,s)t=(3,u,x,s), where uu, xx, and ss have the same meaning as for addr_std\texttt{addr\_std}.
The following primitives, which use the above conventions, are defined:
  • FA40\texttt{FA40}LDMSGADDR\texttt{LDMSGADDR} (ss - ss' ss''), loads from CellSlice ss the only prefix that is a valid MsgAddress\texttt{MsgAddress}, and returns both this prefix ss' and the remainder ss'' of ss as CellSlices.
  • FA41\texttt{FA41}LDMSGADDRQ\texttt{LDMSGADDRQ} (ss - ss' ss'' 1-1 or ss 00), a quiet version of LDMSGADDR\texttt{LDMSGADDR}: on success, pushes an extra 1-1; on failure, pushes the original ss and a zero.
  • FA42\texttt{FA42}PARSEMSGADDR\texttt{PARSEMSGADDR} (ss - tt), decomposes CellSlice ss containing a valid MsgAddress\texttt{MsgAddress} into a Tuple tt with separate fields of this MsgAddress\texttt{MsgAddress}. If ss is not a valid MsgAddress\texttt{MsgAddress}, a cell deserialization exception is thrown.
  • FA43\texttt{FA43}PARSEMSGADDRQ\texttt{PARSEMSGADDRQ} (ss - tt 1-1 or 00), a quiet version of PARSEMSGADDR\texttt{PARSEMSGADDR}: returns a zero on error instead of throwing an exception.
  • FA44\texttt{FA44}REWRITESTDADDR\texttt{REWRITESTDADDR} (ss - xx yy), parses CellSlice ss containing a valid MsgAddressInt\texttt{MsgAddressInt} (usually a msg_addr_std\texttt{msg\_addr\_std}), applies rewriting from the anycast\texttt{anycast} (if present) to the same-length prefix of the address, and returns both the workchain xx and the 256-bit address yy as Integers. If the address is not 256-bit, or if ss is not a valid serialization of MsgAddressInt\texttt{MsgAddressInt}, throws a cell deserialization exception.
  • FA45\texttt{FA45}REWRITESTDADDRQ\texttt{REWRITESTDADDRQ} (ss - xx yy 1-1 or 00), a quiet version of primitive REWRITESTDADDR\texttt{REWRITESTDADDR}.
  • FA46\texttt{FA46}REWRITEVARADDR\texttt{REWRITEVARADDR} (ss - xx ss'), a variant of REWRITESTDADDR\texttt{REWRITESTDADDR} that returns the (rewritten) address as a Slice s, even if it is not exactly 256 bit long (represented by a msg_addr_var\texttt{msg\_addr\_var}).
  • FA47\texttt{FA47}REWRITEVARADDRQ\texttt{REWRITEVARADDRQ} (ss - xx ss' 1-1 or 00), a quiet version of primitive REWRITEVARADDR\texttt{REWRITEVARADDR}.
  • FA48\texttt{FA48}-FA5F\texttt{FA5F} — Reserved for message and address manipulation primitives.

A.11.10. Outbound message and output action primitives

  • FB00\texttt{FB00}SENDRAWMSG\texttt{SENDRAWMSG} (cc xx - ), sends a raw message contained in Cell cc, which should contain a correctly serialized object Message\texttt{Message} XX, with the only exception that the source address is allowed to have dummy value addr_none\texttt{addr\_none} (to be automatically replaced with the current smart-contract address), and ihr_fee\texttt{ihr\_fee}, fwd_fee\texttt{fwd\_fee}, created_lt\texttt{created\_lt} and created_at\texttt{created\_at} fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter xx contains the flags. Currently x=0x=0 is used for ordinary messages; x=128x=128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); x=64x=64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); x=x+1x'=x+1 means that the sender wants to pay transfer fees separately; x=x+2x'=x+2 means that any errors arising while processing this message during the action phase should be ignored. Finally, x=x+32x'=x+32 means that the current account must be destroyed if its resulting balance is zero. This flag is usually employed together with +128+128.
  • FB02\texttt{FB02}RAWRESERVE\texttt{RAWRESERVE} (xx yy - ), creates an output action which would reserve exactly xx nanograms (if y=0y=0), at most xx nanograms (if y=2y=2), or all but xx nanograms (if y=1y=1 or y=3y=3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying xx nanograms (or bxb-x nanograms, where bb is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2+2 in yy means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8+8 in yy means xxx\leftarrow -x before performing any further actions. Bit +4+4 in yy means that xx is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently xx must be a non-negative integer, and yy must be in the range 0150\ldots 15.
  • FB03\texttt{FB03}RAWRESERVEX\texttt{RAWRESERVEX} (xx DD yy - ), similar to RAWRESERVE\texttt{RAWRESERVE}, but also accepts a dictionary DD (represented by a Cell or Null) with extra currencies. In this way currencies other than Grams can be reserved.
  • FB04\texttt{FB04}SETCODE\texttt{SETCODE} (cc - ), creates an output action that would change this smart contract code to that given by Cell cc. Notice that this change will take effect only after the successful termination of the current run of the smart contract.
  • FB06\texttt{FB06}SETLIBCODE\texttt{SETLIBCODE} (cc xx - ), creates an output action that would modify the collection of this smart contract libraries by adding or removing library with code given in Cell cc. If x=0x=0, the library is actually removed if it was previously present in the collection (if not, this action does nothing). If x=1x=1, the library is added as a private library, and if x=2x=2, the library is added as a public library (and becomes available to all smart contracts if the current smart contract resides in the masterchain); if the library was present in the collection before, its public/private status is changed according to xx. Values of xx other than 020\ldots 2 are invalid.
  • FB07\texttt{FB07}CHANGELIB\texttt{CHANGELIB} (hh xx - ), creates an output action similarly to SETLIBCODE\texttt{SETLIBCODE}, but instead of the library code accepts its hash as an unsigned 256-bit integer hh. If x0x\neq0 and the library with hash hh is absent from the library collection of this smart contract, this output action will fail.
  • FB08\texttt{FB08}-FB3F\texttt{FB3F} — Reserved for output action primitives.

A.12 Debug primitives

Opcodes beginning with FE\texttt{FE} are reserved for the debug primitives. These primitives have known fixed operation length, and behave as (multibyte) NOP operations. In particular, they never change the stack contents, and never throw exceptions, unless there are not enough bits to completely decode the opcode. However, when invoked in a TVM instance with debug mode enabled, these primitives can produce specific output into the text debug log of the TVM instance, never affecting the TVM state (so that from the perspective of TVM the behavior of debug primitives in debug mode is exactly the same). For instance, a debug primitive might dump all or some of the values near the top of the stack, display the current state of TVM and so on.

A.12.1. Debug primitives as multibyte NOPs

  • FEnn\texttt{FE}nnDEBUG\texttt{DEBUG} nnnn, for 0nn<2400\leq nn<240, is a two-byte NOP.
  • FEFnssss\texttt{FEF}nssssDEBUGSTR\texttt{DEBUGSTR} ssssssss, for 0n<160\leq n<16, is an (n+3)(n+3)-byte NOP, with the (n+1)(n+1)-byte “contents string” ssssssss skipped as well.

A.12.2. Debug primitives as operations without side-effect

Next we describe the debug primitives that might (and actually are) implemented in a version of TVM. Notice that another TVM implementation is free to use these codes for other debug purposes, or treat them as multibyte NOPs. Whenever these primitives need some arguments from the stack, they inspect these arguments, but leave them intact in the stack. If there are insufficient values in the stack, or they have incorrect types, debug primitives may output error messages into the debug log, or behave as NOPs, but they cannot throw exceptions.
  • FE00\texttt{FE00}DUMPSTK\texttt{DUMPSTK}, dumps the stack (at most the top 255 values) and shows the total stack depth.
  • FE0n\texttt{FE0}nDUMPSTKTOP\texttt{DUMPSTKTOP} nn, 1n<151\leq n<15, dumps the top nn values from the stack, starting from the deepest of them. If there are d<nd<n values available, dumps only dd values.
  • FE10\texttt{FE10}HEXDUMP\texttt{HEXDUMP}, dumps s0\texttt{s0} in hexadecimal form, be it a Slice or an Integer.
  • FE11\texttt{FE11}HEXPRINT\texttt{HEXPRINT}, similar to HEXDUMP\texttt{HEXDUMP}, except the hexadecimal representation of s0\texttt{s0} is not immediately output, but rather concatenated to an output text buffer.
  • FE12\texttt{FE12}BINDUMP\texttt{BINDUMP}, dumps s0\texttt{s0} in binary form, similarly to HEXDUMP\texttt{HEXDUMP}.
  • FE13\texttt{FE13}BINPRINT\texttt{BINPRINT}, outputs the binary representation of s0\texttt{s0} to a text buffer.
  • FE14\texttt{FE14}STRDUMP\texttt{STRDUMP}, dumps the Slice at s0\texttt{s0} as an UTF-8 string.
  • FE15\texttt{FE15}STRPRINT\texttt{STRPRINT}, similar to STRDUMP\texttt{STRDUMP}, but outputs the string into a text buffer (without carriage return).
  • FE1E\texttt{FE1E}DEBUGOFF\texttt{DEBUGOFF}, disables all debug output until it is re-enabled by a DEBUGON\texttt{DEBUGON}. More precisely, this primitive increases an internal counter, which disables all debug operations (except DEBUGOFF\texttt{DEBUGOFF} and DEBUGON\texttt{DEBUGON}) when strictly positive.
  • FE1F\texttt{FE1F}DEBUGON\texttt{DEBUGON}, enables debug output (in a debug version of TVM).
  • FE2n\texttt{FE2}nDUMP s(n)\texttt{DUMP s}(n), 0n<150\leq n<15, dumps s(n)\texttt{s}(n).
  • FE3n\texttt{FE3}nPRINT s(n)\texttt{PRINT s}(n), 0n<150\leq n<15, concatenates the text representation of s(n)\texttt{s}(n) (without any leading or trailing spaces or carriage returns) to a text buffer which will be output before the output of any other debug operation.
  • FEC0\texttt{FEC0}-FEEF\texttt{FEEF} — Use these opcodes for custom/experimental debug operations.
  • FEFnssss\texttt{FEF}nssssDUMPTOSFMT\texttt{DUMPTOSFMT} ssssssss, dumps s0\texttt{s0} formatted according to the (n+1)(n+1)-byte string ssssssss. This string might contain (a prefix of) the name of a TL-B type supported by the debugger. If the string begins with a zero byte, simply outputs it (without the first byte) into the debug log. If the string begins with a byte equal to one, concatenates it to a buffer, which will be output before the output of any other debug operation (effectively outputs a string without a carriage return).
  • FEFn00ssss\texttt{FEF}n\texttt{00}ssssLOGSTR\texttt{LOGSTR} ssssssss, string ssssssss is nn bytes long.
  • FEF000\texttt{FEF000}LOGFLUSH\texttt{LOGFLUSH}, flushes all pending debug output from the buffer into the debug log.
  • FEFn01ssss\texttt{FEF}n\texttt{01}ssssPRINTSTR\texttt{PRINTSTR} ssssssss, string ssssssss is nn bytes long.

A.13. Codepage primitives

The following primitives, which begin with byte FF\texttt{FF}, typically are used at the very beginning of a smart contract’s code or a library subroutine to select another TVM codepage. Notice that we expect all codepages to contain these primitives with the same codes, otherwise switching back to another codepage might be impossible (cf. 5.1.8).
  • FFnn\texttt{FF}nnSETCP\texttt{SETCP} nnnn, selects TVM codepage 0nn<2400\leq nn<240. If the codepage is not supported, throws an invalid opcode exception.
  • FF00\texttt{FF00}SETCP0\texttt{SETCP0}, selects TVM (test) codepage zero as described in this document.
  • FFFz\texttt{FFF}zSETCP\texttt{SETCP} z16z-16, selects TVM codepage z16z-16 for 1z151\leq z\leq 15. Negative codepages 131-13\ldots-1 are reserved for restricted versions of TVM needed to validate runs of TVM in other codepages as explained in B.2.6. Negative codepage 14-14 is reserved for experimental codepages, not necessarily compatible between different TVM implementations, and should be disabled in the production versions of TVM.
  • FFF0\texttt{FFF0}SETCPX\texttt{SETCPX} (cc - ), selects codepage cc with 215c<215-2^{15}\leq c<2^{15} passed in the top of the stack.

B Formal properties and specifications of TVM

This appendix discusses certain formal properties of TVM that are necessary for executing smart contracts in the TON Blockchain and validating such executions afterwards.

B.1 Serialization of the TVM state

Recall that a virtual machine used for executing smart contracts in a blockchain must be deterministic, otherwise the validation of each execution would require the inclusion of all intermediate steps of the execution into a block, or at least of the choices made when indeterministic operations have been performed. Furthermore, the state of such a virtual machine must be (uniquely) serializable, so that even if the state itself is not usually included in a block, its hash is still well-defined and can be included into a block for verification purposes.

B.1.1. TVM stack values

TVM stack values can be serialized as follows:
vm_stk_tinyint#01 value:int64 = VmStackValue;
vm_stk_int#0201_ value:int257 = VmStackValue;
vm_stk_nan#02FF = VmStackValue;
vm_stk_cell#03 cell:^Cell = VmStackValue;
_ cell:^Cell st_bits:(## 10) end_bits:(## 10) 
  { st_bits <= end_bits } 
  st_ref:(#<= 4) end_ref:(#<= 4) 
  { st_ref <= end_ref } = VmCellSlice;
vm_stk_slice#04 _:VmCellSlice  = VmStackValue;
vm_stk_builder#05 cell:^Cell = VmStackValue;
vm_stk_cont#06 cont:VmCont = VmStackValue;
Of these, vm_stk_tinyint\texttt{vm\_stk\_tinyint} is never used by TVM in codepage zero; it is used only in restricted modes.

B.1.2. TVM stack

The TVM stack can be serialized as follows:
vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack;
vm_stk_cons#_ {n:#} head:VmStackValue tail:^(VmStackList n) 
  = VmStackList (n + 1);
vm_stk_nil#_ = VmStackList 0;

B.1.3. TVM control registers

Control registers in TVM can be serialized as follows:
_ cregs:(HashmapE 4 VmStackValue) = VmSaveList;

B.1.4. TVM gas limits

Gas limits in TVM can be serialized as follows:
gas_limits#_ remaining:int64 _:^[ 
  max_limit:int64 cur_limit:int64 credit:int64 ]
  = VmGasLimits;

B.1.5. TVM library environment

The TVM library environment can be serialized as follows:
_ libraries:(HashmapE 256 ^Cell) = VmLibraries;

B.1.6. TVM continuations

Continuations in TVM can be serialized as follows:
vmc_std$00 nargs:(## 22) stack:(Maybe VmStack) save:VmSaveList
   cp:int16 code:VmCellSlice = VmCont;
vmc_envelope$01 nargs:(## 22) stack:(Maybe VmStack) 
   save:VmSaveList next:^VmCont = VmCont;
vmc_quit$1000 exit_code:int32 = VmCont;
vmc_quit_exc$1001 = VmCont;
vmc_until$1010 body:^VmCont after:^VmCont = VmCont;
vmc_again$1011 body:^VmCont = VmCont;
vmc_while_cond$1100 cond:^VmCont body:^VmCont 
   after:^VmCont = VmCont;
vmc_while_body$1101 cond:^VmCont body:^VmCont
   after:^VmCont = VmCont;
vmc_pushint$1111 value:int32 next:^VmCont = VmCont;

B.1.7. TVM state

The total state of TVM can be serialized as follows:
vms_init$00 cp:int16 step:int32 gas:GasLimits 
  stack:(Maybe VmStack) save:VmSaveList code:VmCellSlice
  lib:VmLibraries = VmState;
vms_exception$01 cp:int16 step:int32 gas:GasLimits 
  exc_no:int32 exc_arg:VmStackValue 
  save:VmSaveList lib:VmLibraries = VmState;
vms_running$10 cp:int16 step:int32 gas:GasLimits stack:VmStack
  save:VmSaveList code:VmCellSlice lib:VmLibraries
  = VmState;
vms_finished$11 cp:int16 step:int32 gas:GasLimits 
  exit_code:int32 no_gas:Boolean stack:VmStack 
  save:VmSaveList lib:VmLibraries = VmState;
When TVM is initialized, its state is described by a vms_init\texttt{vms\_init}, usually with step\texttt{step} set to zero. The step function of TVM does nothing to a vms_finished\texttt{vms\_finished} state, and transforms all other states into vms_running\texttt{vms\_running}, vms_exception\texttt{vms\_exception}, or vms_finished\texttt{vms\_finished}, with step\texttt{step} increased by one.

B.2 Step function of TVM

A formal specification of TVM would be completed by the definition of a step function f:VmStateVmStatef:\textit{VmState}\to\textit{VmState}. This function deterministically transforms a valid VM state into a valid subsequent VM state, and is allowed to throw exceptions or return an invalid subsequent state if the original state was invalid.

B.2.1. A high-level definition of the step function

We might present a very long formal definition of the TVM step function in a high-level functional programming language. Such a specification, however, would mostly be useful as a reference for the (human) developers. We have chosen another approach, better adapted to automated formal verification by computers.

B.2.2. An operational definition of the step function

Notice that the step function ff is a well-defined computable function from trees of cells into trees of cells. As such, it can be computed by a universal Turing machine. Then a program PP computing ff on such a machine would provide a machine-checkable specification of the step function ff. This program PP effectively is an emulator of TVM on this Turing machine.

B.2.3. A reference implementation of the TVM emulator inside TVM

We see that the step function of TVM may be defined by a reference implementation of a TVM emulator on another machine. An obvious idea is to use TVM itself, since it is well-adapted to working with trees of cells. However, an emulator of TVM inside itself is not very useful if we have doubts about a particular implementation of TVM and want to check it. For instance, if such an emulator interpreted a DICTISET\texttt{DICTISET} instruction simply by invoking this instruction itself, then a bug in the underlying implementation of TVM would remain unnoticed.

B.2.4. Reference implementation inside a minimal version of TVM

We see that using TVM itself as a host machine for a reference implementation of TVM emulator would yield little insight. A better idea is to define a stripped-down version of TVM, which supports only the bare minimum of primitives and 64-bit integer arithmetic, and provide a reference implementation PP of the TVM step function ff for this stripped-down version of TVM. In that case, one must carefully implement and check only a handful of primitives to obtain a stripped-down version of TVM, and compare the reference implementation PP running on this stripped-down version to the full custom TVM implementation being verified. In particular, if there are any doubts about the validity of a specific run of a custom TVM implementation, they can now be easily resolved with the aid of the reference implementation.

B.2.5. Relevance for the TON Blockchain

The TON Blockchain adopts this approach to validate the runs of TVM (e.g., those used for processing inbound messages by smart contracts) when the validators’ results do not match one another. In this case, a reference implementation of TVM, stored inside the masterchain as a configurable parameter (thus defining the current revision of TVM), is used to obtain the correct result.

B.2.6. Codepage 1-1

Codepage 1-1 of TVM is reserved for the stripped-down version of TVM. Its main purpose is to execute the reference implementation of the step function of the full TVM. This codepage contains only special versions of arithmetic primitives working with “tiny integers” (64-bit signed integers); therefore, TVM’s 257-bit Integer arithmetic must be defined in terms of 64-bit arithmetic. Elliptic curve cryptography primitives are also implemented directly in codepage 1-1, without using any third-party libraries. Finally, a reference implementation of the Sha\texttt{Sha} hash function is also provided in codepage 1-1.

B.2.7. Codepage 2-2

This bootstrapping process could be iterated even further, by providing an emulator of the stripped-down version of TVM written for an even simpler version of TVM that supports only boolean values (or integers 0 and 1)—a “codepage 2-2”. All 64-bit arithmetic used in codepage 1-1 would then need to be defined by means of boolean operations, thus providing a reference implementation for the stripped-down version of TVM used in codepage 1-1. In this way, if some of the TON Blockchain validators did not agree on the results of their 64-bit arithmetic, they could regress to this reference implementation to find the correct answer.30

C Code density of stack and register machines

This appendix extends the general consideration of stack manipulation primitives provided in 2.2, explaining the choice of such primitives for TVM, with a comparison of stack machines and register machines in terms of the quantity of primitives used and the code density. We do this by comparing the machine code that might be generated by an optimizing compiler for the same source files, for different (abstract) stack and register machines. It turns out that the stack machines (at least those equipped with the basic stack manipulation primitives described in 2.2.1) have far superior code density. Furthermore, the stack machines have excellent extendability with respect to additional arithmetic and arbitrary data processing operations, especially if one considers machine code automatically generated by optimizing compilers.

C.1 Sample leaf function

We start with a comparison of machine code generated by an (imaginary) optimizing compiler for several abstract register and stack machines, corresponding to the same high-level language source code that contains the definition of a leaf function (i.e., a function that does not call any other functions). For both the register machines and stack machines, we observe the notation and conventions introduced in 2.1.

C.1.1. Sample source file for a leaf function

The source file we consider contains one function ff that takes six integer arguments aa, bb, cc, dd, ee, and ff and returns two integer values, xx and yy, which are the solutions of the system of two linear equations: {ax+by=ecx+dy=f(6)\begin{cases} ax + by = e \\ cx + dy = f \end{cases} \tag{6} The source code of the function, in a programming language similar to C, might look as follows:
(int, int) f(int a, int b, int c, int d, int e, int f) {
  int D = a*d - b*c;
  int Dx = e*d - b*f;
  int Dy = a*f - e*c;
  return (Dx / D, Dy / D);
}
We assume (cf. 2.1) that the register machines we consider accept the six parameters aa \ldots ff in registers r0\texttt{r0} \ldots r5\texttt{r5}, and return the two values xx and yy in r0\texttt{r0} and r1\texttt{r1}. We also assume that the register machines have 16 registers, and that the stack machine can directly access s0\texttt{s0} to s15\texttt{s15} by its stack manipulation primitives; the stack machine will accept the parameters in s5\texttt{s5} to s0\texttt{s0}, and return the two values in s0\texttt{s0} and s1\texttt{s1}, somewhat similarly to the register machine. Finally, we assume at first that the register machine is allowed to destroy values in all registers (which is slightly unfair towards the stack machine); this assumption will be revisited later.

C.1.2. Three-address register machine

The machine code (or rather the corresponding assembly code) for a three-address register machine (cf. 2.1.7) might look as follows:
IMUL r6,r0,r3  // r6 := r0 * r3 = ad
IMUL r7,r1,r2  // r7 := bc
SUB r6,r6,r7   // r6 := ad-bc = D
IMUL r3,r4,r3  // r3 := ed
IMUL r1,r1,r5  // r1 := bf
SUB r3,r3,r1   // r3 := ed-bf = Dx
IMUL r1,r0,r5  // r1 := af
IMUL r7,r4,r2  // r7 := ec
SUB r1,r1,r7   // r1 := af-ec = Dy
IDIV r0,r3,r6  // x := Dx/D
IDIV r1,r1,r6  // y := Dy/D
RET
We have used 12 operations and at least 23 bytes (each operation uses 3×4=123\times 4=12 bits to indicate the three registers involved, and at least 4 bits to indicate the operation performed; thus we need two or three bytes to encode each operation). A more realistic estimate would be 34 (three bytes for each arithmetic operation) or 31 bytes (two bytes for addition and subtraction, three bytes for multiplication and division).

C.1.3. Two-address register machine

The machine code for a two-address register machine might look as follows:
MOV r6,r0   // r6 := r0 = a
MOV r7,r1   // r7 := b
IMUL r6,r3  // r6 := r6*r3 = ad
IMUL r7,r2  // r7 := bc
IMUL r3,r4  // r3 := de
IMUL r1,r5  // r1 := bf
SUB r6,r7   // r6 := ad-bc = D
IMUL r5,r0  // r5 := af
SUB r3,r1   // r3 := de-bf = Dx
IMUL r2,r4  // r2 := ce
MOV r0,r3   // r0 := Dx
SUB r5,r2   // r5 := af-ce = Dy
IDIV r0,r6  // r0 := x = Dx/D
MOV r1,r5   // r1 := Dy
IDIV r1,r6  // r1 := Dy/D
RET
We have used 16 operations; optimistically assuming each of them (with the exception of RET\texttt{RET}) can be encoded by two bytes, this code would require 31 bytes.31

C.1.4. One-address register machine

The machine code for a one-address register machine might look as follows:
MOV r8,r0  // r8 := r0 = a
XCHG r1    // r0 <-> r1; r0 := b, r1 := a
MOV r6,r0  // r6 := b
IMUL r2    // r0 := r0*r2; r0 := bc
MOV r7,r0  // r7 := bc
MOV r0,r8  // r0 := a
IMUL r3    // r0 := ad
SUB r7     // r0 := ad-bc = D
XCHG r1    // r1 := D,  r0 := b
IMUL r5    // r0 := bf
XCHG r3    // r0 := d,  r3 := bf
IMUL r4    // r0 := de
SUB r3     // r0 := de-bf = Dx
IDIV r1    // r0 := Dx/D = x
XCHG r2    // r0 := c,  r2 := x
IMUL r4    // r0 := ce
XCHG r5    // r0 := f,  r5 := ce
IMUL r8    // r0 := af
SUB r5     // r0 := af-ce = Dy
IDIV r1    // r0 := Dy/D = y
MOV r1,r0  // r1 := y
MOV r0,r2  // r0 := x
RET
We have used 23 operations; if we assume one-byte encoding for all arithmetic operations and XCHG\texttt{XCHG}, and two-byte encodings for MOV\texttt{MOV}, the total size of the code will be 29 bytes. Notice, however, that to obtain the compact code shown above we had to choose a specific order of computation, and made heavy use of the commutativity of multiplication. (For example, we compute bcbc before afaf, and afbcaf-bc immediately after afaf.) It is not clear whether a compiler would be able to make all such optimizations by itself.

C.1.5. Stack machine with basic stack primitives

The machine code for a stack machine equipped with basic stack manipulation primitives described in 2.2.1 might look as follows:
PUSH s5    // a b c d e f a
PUSH s3    // a b c d e f a d
IMUL       // a b c d e f ad
PUSH s5    // a b c d e f ad b
PUSH s5    // a b c d e f ad b c
IMUL       // a b c d e f ad bc
SUB        // a b c d e f ad-bc
XCHG s3    // a b c ad-bc e f d
PUSH s2    // a b c ad-bc e f d e
IMUL       // a b c ad-bc e f de
XCHG s5    // a de c ad-bc e f b
PUSH s1    // a de c ad-bc e f b f
IMUL       // a de c ad-bc e f bf
XCHG s1,s5 // a f c ad-bc e de bf
SUB        // a f c ad-bc e de-bf
XCHG s3    // a f de-bf ad-bc e c
IMUL       // a f de-bf ad-bc ec
XCHG s3    // a ec de-bf ad-bc f
XCHG s1,s4 // ad-bc ec de-bf a f
IMUL       // D ec Dx af
XCHG s1    // D ec af Dx
XCHG s2    // D Dx af ec
SUB        // D Dx Dy
XCHG s1    // D Dy Dx
PUSH s2    // D Dy Dx D
IDIV       // D Dy x
XCHG s2    // x Dy D
IDIV       // x y
RET
We have used 29 operations; assuming one-byte encodings for all stack operations involved (including XCHG s1,s(i)\texttt{XCHG s1,s}(i)), we have used 29 code bytes as well. Notice that with one-byte encoding, the “unsystematic” operation ROT\texttt{ROT} (equivalent to XCHG s1\texttt{XCHG s1}; XCHG s2\texttt{XCHG s2}) would reduce the operation and byte count to 28. This shows that such “unsystematic” operations, borrowed from Forth, may indeed reduce the code size on some occasions. Notice as well that we have implicitly used the commutativity of multiplication in this code, computing debfde-bf instead of edbfed-bf as specified in high-level language source code. If we were not allowed to do so, an extra XCHG s1\texttt{XCHG s1} would need to be inserted before the third IMUL\texttt{IMUL}, increasing the total size of the code by one operation and one byte. The code presented above might have been produced by a rather unsophisticated compiler that simply computed all expressions and subexpressions in the order they appear, then rearranged the arguments near the top of the stack before each operation as outlined in 2.2.2. The only “manual” optimization done here involves computing ecec before afaf; one can check that the other order would lead to slightly shorter code of 28 operations and bytes (or 29, if we are not allowed to use the commutativity of multiplication), but the ROT\texttt{ROT} optimization would not be applicable.

C.1.6. Stack machine with compound stack primitives

A stack machine with compound stack primitives (cf. 2.2.3) would not significantly improve code density of the code presented above, at least in terms of bytes used. The only difference is that, if we were not allowed to use commutativity of multiplication, the extra XCHG s1\texttt{XCHG s1} inserted before the third IMUL\texttt{IMUL} might be combined with two previous operations XCHG s3\texttt{XCHG s3}, PUSH s2\texttt{PUSH s2} into one compound operation PUXC s2,s3\texttt{PUXC s2,s3}; we provide the resulting code below. To make this less redundant, we show a version of the code that computes subexpression afaf before ecec as specified in the original source file. We see that this replaces six operations (starting from line 15) with five other operations, and disables the ROT\texttt{ROT} optimization:
PUSH s5    // a b c d e f a
PUSH s3    // a b c d e f a d
IMUL       // a b c d e f ad
PUSH s5    // a b c d e f ad b
PUSH s5    // a b c d e f ad b c
IMUL       // a b c d e f ad bc
SUB        // a b c d e f ad-bc
PUXC s2,s3 // a b c ad-bc e f e d
IMUL       // a b c ad-bc e f ed
XCHG s5    // a ed c ad-bc e f b
PUSH s1    // a ed c ad-bc e f b f
IMUL       // a ed c ad-bc e f bf
XCHG s1,s5 // a f c ad-bc e ed bf
SUB        // a f c ad-bc e ed-bf
XCHG s4    // a ed-bf c ad-bc e f
XCHG s1,s5 // e Dx c D a f
IMUL       // e Dx c D af
XCHG s2    // e Dx af D c
XCHG s1,s4 // D Dx af e c
IMUL       // D Dx af ec
SUB        // D Dx Dy
XCHG s1    // D Dy Dx
PUSH s2    // D Dy Dx D
IDIV       // D Dy x
XCHG s2    // x Dy D
IDIV       // x y
RET
We have used a total of 27 operations and 28 bytes, the same as the previous version (with the ROT\texttt{ROT} optimization). However, we did not use the commutativity of multiplication here, so we can say that compound stack manipulation primitives enable us to reduce the code size from 29 to 28 bytes. Yet again, notice that the above code might have been generated by an unsophisticated compiler. Manual optimizations might lead to more compact code; for instance, we could use compound operations such as XCHG3\texttt{XCHG3} to prepare in advance not only the correct values of s0\texttt{s0} and s1\texttt{s1} for the next arithmetic operation, but also the value of s2\texttt{s2} for the arithmetic operation after that. The next section provides an example of such an optimization.

C.1.7. Stack machine with compound stack primitives and manually optimized code

The previous version of code for a stack machine with compound stack primitives can be manually optimized as follows. By interchanging XCHG\texttt{XCHG} operations with preceding XCHG\texttt{XCHG}, PUSH\texttt{PUSH}, and arithmetic operations whenever possible, we obtain code fragment XCHG s2,s6\texttt{XCHG s2,s6}; XCHG s1,s0\texttt{XCHG s1,s0}; XCHG s0,s5\texttt{XCHG s0,s5}, which can then be replaced by compound operation XCHG3 s6,s0,s5\texttt{XCHG3 s6,s0,s5}. This compound operation would admit a two-byte encoding, thus leading to 27-byte code using only 21 operations:
PUSH2 s5,s2    // a b c d e f a d
IMUL           // a b c d e f ad
PUSH2 s5,s4    // a b c d e f ad b c
IMUL           // a b c d e f ad bc
SUB            // a b c d e f ad-bc
PUXC s2,s3     // a b c ad-bc e f e d
IMUL           // a b c D e f ed
XCHG3 s6,s0,s5 //  (same as XCHG s2,s6; XCHG s1,s0; XCHG s0,s5)
               // e f c D a ed b
PUSH s5        // e f c D a ed b f
IMUL           // e f c D a ed bf
SUB            // e f c D a ed-bf
XCHG s4        // e Dx c D a f
IMUL           // e Dx c D af
XCHG2 s4,s2    // D Dx af e c
IMUL           // D Dx af ec
SUB            // D Dx Dy
XCPU s1,s2     // D Dy Dx D
IDIV           // D Dy x
XCHG s2        // x Dy D
IDIV           // x y
RET
It is interesting to note that this version of stack machine code contains only 9 stack manipulation primitives for 11 arithmetic operations. It is not clear, however, whether an optimizing compiler would be able to reorganize the code in such a manner by itself.

C.2 Comparison of machine code for sample leaf function

Table 1 summarizes the properties of machine code corresponding to the same source file described in C.1.1, generated for a hypothetical three-address register machine (cf. C.1.2), with both “optimistic” and “realistic” instruction encodings; a two-address machine (cf. C.1.3); a one-address machine (cf. C.1.4); and a stack machine, similar to TVM, using either only the basic stack manipulation primitives (cf. C.1.5) or both the basic and the composite stack primitives (cf. C.1.7). The meaning of the columns in Table 1 is as follows:
  • “Operations” — The quantity of instructions used, split into “data” (i.e., register move and exchange instructions for register machines, and stack manipulation instructions for stack machines) and “arithmetic” (instructions for adding, subtracting, multiplying and dividing integer numbers). The “total” is one more than the sum of these two, because there is also a one-byte RET\texttt{RET} instruction at the end of machine code.
  • “Code bytes” — The total amount of code bytes used.
  • “Opcode space” — The portion of “opcode space” (i.e., of possible choices for the first byte of the encoding of an instruction) used by data and arithmetic instructions in the assumed instruction encoding. For example, the “optimistic” encoding for the three-address machine assumes two-byte encodings for all arithmetic instructions op r(i), r(j), r(k)\texttt{r}(i)\texttt{, r}(j)\texttt{, r}(k). Each arithmetic instruction would then consume portion 16/256=1/1616/256=1/16 of the opcode space. Notice that for the stack machine we have assumed one-byte encodings for XCHG s(i)\texttt{XCHG s}(i), PUSH s(i)\texttt{PUSH s}(i) and POP s(i)\texttt{POP s}(i) in all cases, augmented by XCHG s1,s(i)\texttt{XCHG s1,s}(i) for the basic stack instructions case only. As for the compound stack operations, we have assumed two-byte encodings for PUSH3\texttt{PUSH3}, XCHG3\texttt{XCHG3}, XCHG2\texttt{XCHG2}, XCPU\texttt{XCPU}, PUXC\texttt{PUXC}, PUSH2\texttt{PUSH2}, but not for XCHG s1,s(i)\texttt{XCHG s1,s}(i).
MachineOperationsCode bytesOpcode space
dataarithtotaldataarithtotaldataarithtotal
3-addr. (opt.)01112022230/25664/25665/256
3-addr. (real.)01112030310/25634/25635/256
2-addr.41116822311/2564/2566/256
1-addr.11112317112917/25664/25682/256
stack (basic)16112816112864/2564/25669/256
stack (comp.)9112115112784/2564/25689/256
Table 1. A summary of machine code properties for hypothetical 3-address, 2-address, 1-address, and stack machines, generated for a sample leaf function (cf. C.1.1). The two most important columns, reflecting code density and extendability to other operations, are marked by bold font. Smaller values are better in both of these columns. The “code bytes” column reflects the density of the code for the specific sample source. However, “opcode space” is also important, because it reflects the extendability of the achieved density to other classes of operations (e.g., if one were to complement arithmetic operations with string manipulation operations and so on). Here the “arithmetic” subcolumn is more important than the “data” subcolumn, because no further data manipulation operations would be required for such extensions. We see that the three-address register machine with the “optimistic” encoding, assuming two-byte encodings for all three-register arithmetic operations, achieves the best code density, requiring only 23 bytes. However, this comes at a price: each arithmetic operation consumes 1/16 of the opcode space, so the four operations already use a quarter of the opcode space. At most 11 other operations, arithmetic or not, might be added to this architecture while preserving such high code density. On the other hand, when we consider the “realistic” encoding for the three-address machine, using two-byte encodings only for the most frequently used addition/subtraction operations (and longer encodings for less frequently used multiplication/division operations, reflecting the fact that the possible extension operations would likely fall in this class), then the three-address machine ceases to offer such attractive code density. In fact, the two-address machine becomes equally attractive at this point: it is capable of achieving the same code size of 31 bytes as the three-address machine with the “realistic” encoding, using only 6/256 of the opcode space for this! However, 31 bytes is the worst result in this table. The one-address machine uses 29 bytes, slightly less than the two-address machine. However, it utilizes a quarter of the opcode space for its arithmetic operations, hampering its extendability. In this respect it is similar to the three-address machine with the “optimistic” encoding, but requires 29 bytes instead of 23! So there is no reason to use the one-address machine at all, in terms of extendability (reflected by opcode space used for arithmetic operations) compared to code density. Finally, the stack machine wins the competition in terms of code density (27 or 28 bytes), losing only to the three-address machine with the “optimistic” encoding (which, however, is terrible in terms of extendability). To summarize: the two-address machine and stack machine achieve the best extendability with respect to additional arithmetic or data processing instructions (using only 1/256 of code space for each such instruction), while the stack machine additionally achieves the best code density by a small margin. The stack machine utilizes a significant part of its code space (more than a quarter) for data (i.e., stack) manipulation instructions; however, this does not seriously hamper extendability, because the stack manipulation instructions occupy a constant part of the opcode space, regardless of all other instructions and extensions. While one might still be tempted to use a two-address register machine, we will explain shortly (cf. C.3) why the two-address register machine offers worse code density and extendability in practice than it appears based on this table. As for the choice between a stack machine with only basic stack manipulation primitives or one supporting compound stack primitives as well, the case for the more sophisticated stack machine appears to be weaker: it offers only one or two fewer bytes of code at the expense of using considerably more opcode space for stack manipulation, and the optimized code using these additional instructions is hard for programmers to write and for compilers to automatically generate.

C.2.1. Register calling conventions: some registers must be preserved by functions

Up to this point, we have considered the machine code of only one function, without taking into account the interplay between this function and other functions in the same program. Usually a program consists of more than one function, and when a function is not a “simple” or “leaf” function, it must call other functions. Therefore, it becomes important whether a called function preserves all or at least some registers. If it preserves all registers except those used to return results, the caller can safely keep its local and temporary variables in certain registers; however, the callee needs to save all the registers it will use for its temporary values somewhere (usually into the stack, which also exists on register machines), and then restore the original values. On the other hand, if the called function is allowed to destroy all registers, it can be written in the manner described in C.1.2, C.1.3, and C.1.4, but the caller will now be responsible for saving all its temporary values into the stack before the call, and restoring these values afterwards. In most cases, calling conventions for register machines require preservation of some but not all registers. We will assume that mnm\leq n registers will be preserved by functions (unless they are used for return values), and that these registers are r\texttt{r} (nm)(n-m) \ldots r\texttt{r} (n1)(n-1). Case m=0m=0 corresponds to the case “the callee is free to destroy all registers” considered so far; it is quite painful for the caller. Case m=nm=n corresponds to the case “the callee must preserve all registers”; it is quite painful for the callee, as we will see in a moment. Usually a value of mm around n/2n/2 is used in practice. The following sections consider cases m=0m=0, m=8m=8, and m=16m=16 for our register machines with n=16n=16 registers.

C.2.2. Case m=0m=0: no registers to preserve

This case has been considered and summarized in C.2 and Table 1 above.

C.2.3. Case m=n=16m=n=16: all registers must be preserved

This case is the most painful one for the called function. It is especially difficult for leaf functions like the one we have been considering, which do not benefit at all from the fact that other functions preserve some registers when called—they do not call any functions, but instead must preserve all registers themselves. In order to estimate the consequences of assuming m=n=16m=n=16, we will assume that all our register machines are equipped with a stack, and with one-byte instructions PUSH r(i)\texttt{PUSH r}(i) and POP r(i)\texttt{POP r}(i), which push or pop a register into/from the stack. For example, the three-address machine code provided in C.1.2 destroys the values in registers r2\texttt{r2}, r3\texttt{r3}, r6\texttt{r6}, and r7\texttt{r7}; this means that the code of this function must be augmented by four instructions PUSH r2\texttt{PUSH r2}; PUSH r3\texttt{PUSH r3}; PUSH r6\texttt{PUSH r6}; PUSH r7\texttt{PUSH r7} at the beginning, and by four instructions POP r7\texttt{POP r7}; POP r6\texttt{POP r6}; POP r3\texttt{POP r3}; POP r2\texttt{POP r2} right before the RET\texttt{RET} instruction, in order to restore the original values of these registers from the stack. These four additional PUSH\texttt{PUSH}/POP\texttt{POP} pairs would increase the operation count and code size in bytes by 4×2=84\times 2=8. A similar analysis can be done for other register machines as well, leading to Table 2.
Machiner\mathit{r}OperationsCode bytesOpcode space
dataarithtotaldataarithtotaldataarithtotal
3-addr. (opt.)4811208223132/25664/25697/256
3-addr. (real.)4811208303932/25634/25667/256
2-addr.514112618224133/2564/25638/256
1-addr.623113529114149/25664/256114/256
stack (basic)016112816112864/2564/25669/256
stack (comp.)09112115112784/2564/25689/256
Table 2. A summary of machine code properties for hypothetical 3-address, 2-address, 1-address, and stack machines, generated for a sample leaf function (cf. C.1.1), assuming all of the 16 registers must be preserved by called functions (m=n=16m=n=16). The new column labeled rr denotes the number of registers to be saved and restored, leading to 2r2r more operations and code bytes compared to Table 1. Newly-added PUSH\texttt{PUSH} and POP\texttt{POP} instructions for register machines also utilize 32/256 of the opcode space. The two rows corresponding to stack machines remain unchanged. We see that under these assumptions the stack machines are the obvious winners in terms of code density, and are in the winning group with respect to extendability.

C.2.4. Case m=8m = 8, n=16n = 16: registers r8\texttt{r8} \ldots r15\texttt{r15} must be preserved

The analysis of this case is similar to the previous one. The results are summarized in Table 3.
Machiner\mathit{r}OperationsCode bytesOpcode space
dataarithtotaldataarithtotaldataarithtotal
3-addr. (opt.)0011120222332/25664/25697/256
3-addr. (real.)0011120303132/25634/25667/256
2-addr.0411168223133/2564/25638/256
1-addr.113112519113149/25664/256114/256
stack (basic)016112816112864/2564/25669/256
stack (comp.)09112115112784/2564/25689/256
Table 3. A summary of machine code properties for hypothetical 3-address, 2-address, 1-address and stack machines, generated for a sample leaf function (cf. C.1.1), assuming that only the last 8 of the 16 registers must be preserved by called functions (m=8m=8, n=16n=16). This table is similar to Table 2, but has smaller values of rr. Notice that the resulting table is very similar to Table 1, apart from the “Opcode space” columns and the row for the one-address machine. Therefore, the conclusions of C.2 still apply in this case, with some minor modifications. We must emphasize, however, that these conclusions are valid only for leaf functions, i.e., functions that do not call other functions. Any program aside from the very simplest will have many non-leaf functions, especially if we are minimizing resulting machine code size (which prevents inlining of functions in most cases).

C.2.5. A fairer comparison using a binary code instead of a byte code

The reader may have noticed that our preceding discussion of kk-address register machines and stack machines depended very much on our insistence that complete instructions be encoded by an integer number of bytes. If we had been allowed to use a “bit” or “binary code” instead of a byte code for encoding instructions, we could more evenly balance the opcode space used by different machines. For instance, the opcode of SUB for a three-address machine had to be either 4-bit (good for code density, bad for opcode space) or 12-bit (very bad for code density), because the complete instruction has to occupy a multiple of eight bits (e.g., 16 or 24 bits), and 34=123\cdot 4=12 of those bits have to be used for the three register names. Therefore, let us get rid of this restriction. Now that we can use any number of bits to encode an instruction, we can choose all opcodes of the same length for all the machines considered. For instance, all arithmetic instructions can have 8-bit opcodes, as the stack machine does, using 1/2561/256 of the opcode space each; then the three-address register machine will use 20 bits to encode each complete arithmetic instruction. All MOV\texttt{MOV}s, XCHG\texttt{XCHG}s, PUSH\texttt{PUSH}es, and POP\texttt{POP}s on register machines can be assumed to have 4-bit opcodes, because this is what we do for the most common stack manipulation primitives on a stack machine. The results of these changes are shown in Table 4. We can see that the performance of the various machines is much more balanced, with the stack machine still the winner in terms of the code density, but with the three-address machine enjoying the second place it really merits. If we were to consider the decoding speed and the possibility of parallel execution of instructions, we would have to choose the three-address machine, because it uses only 12 instructions instead of 21.
Machiner\mathit{r}OperationsCode bytesOpcode space
dataarithtotaldataarithtotaldataarithtotal
3-addr.001112027.528.564/2564/25669/256
2-addr.0411166222964/2564/25669/256
1-addr.11311251616.532.564/2564/25669/256
stack (basic)016112816112864/2564/25669/256
stack (comp.)09112115112784/2564/25689/256
Table 4. A summary of machine code properties for hypothetical 3-address, 2-address, 1-address and stack machines, generated for a sample leaf function (cf. C.1.1), assuming that only 8 of the 16 registers must be preserved by functions (m=8m=8, n=16n=16). This time we can use fractions of bytes to encode instructions, so as to match opcode space used by different machines. All arithmetic instructions have 8-bit opcodes, all data/stack manipulation instructions have 4-bit opcodes. In other respects this table is similar to Table 3.

C.3 Sample non-leaf function

This section compares the machine code for different register machines for a sample non-leaf function. Again, we assume that either m=0m=0, m=8m=8, or m=16m=16 registers are preserved by called functions, with m=8m=8 representing the compromise made by most modern compilers and operating systems.

C.3.1. Sample source code for a non-leaf function

A sample source file may be obtained by replacing the built-in integer type with a custom Rational type, represented by a pointer to an object in memory, in our function for solving systems of two linear equations (cf. C.1.1):
struct Rational;
typedef struct Rational *num;
extern num r_add(num, num);
extern num r_sub(num, num);
extern num r_mul(num, num);
extern num r_div(num, num);

(num, num) r_f(num a, num b, num c, num d, num e, num f) {
  num D = r_sub(r_mul(a, d), r_mul(b, c));   // a*d-b*c
  num Dx = r_sub(r_mul(e, d), r_mul(b, f));  // e*d-b*f
  num Dy = r_sub(r_mul(a, f), r_mul(e, c));  // a*f-e*c
  return (r_div(Dx, D), r_div(Dy, D));      // Dx/D, Dy/D
}
We will ignore all questions related to allocating new objects of type Rational in memory (e.g., in heap), and to preventing memory leaks. We may assume that the called subroutines r_sub\texttt{r\_sub}, r_mul\texttt{r\_mul}, and so on allocate new objects simply by advancing some pointer in a pre-allocated buffer, and that unused objects are later freed by a garbage collector, external to the code being analysed. Rational numbers will now be represented by pointers, addresses, or references, which will fit into registers of our hypothetical register machines or into the stack of our stack machines. If we want to use TVM as an instance of these stack machines, we should use values of type Cell to represent such references to objects of type Rational in memory. We assume that subroutines (or functions) are called by a special CALL\texttt{CALL} instruction, which is encoded by three bytes, including the specification of the function to be called (e.g., the index in a “global function table”).

C.3.2. Three-address and two-address register machines, m=0m=0 preserved registers

Because our sample function does not use built-in arithmetic instructions at all, compilers for our hypothetical three-address and two-address register machines will produce the same machine code. Apart from the previously introduced PUSH r(i)\texttt{PUSH r}(i) and POP r(i)\texttt{POP r}(i) one-byte instructions, we assume that our two- and three-address machines support the following two-byte instructions: MOV r(i),s(j)\texttt{MOV r}(i)\texttt{,s}(j), MOV s(j),r(i)\texttt{MOV s}(j)\texttt{,r}(i), and XCHG r(i),s(j)\texttt{XCHG r}(i)\texttt{,s}(j), for 0i,j150\leq i,j\leq 15. Such instructions occupy only 3/256 of the opcode space, so their addition seems quite natural. We first assume that m=0m=0 (i.e., that all subroutines are free to destroy the values of all registers). In this case, our machine code for r_f\texttt{r\_f} does not have to preserve any registers, but has to save all registers containing useful values into the stack before calling any subroutines. A size-optimizing compiler might produce the following code:
PUSH r4     // STACK: e
PUSH r1     // STACK: e b
PUSH r0     //     .. e b a
PUSH r6     //     .. e b a f
PUSH r2     //     .. e b a f c
PUSH r3     //     .. e b a f c d
MOV r0,r1   // b
MOV r1,r2   // c
CALL r_mul  // bc
PUSH r0     //     .. e b a f c d bc
MOV r0,s4   // a
MOV r1,s1   // d
CALL r_mul  // ad
POP r1      // bc; .. e b a f c d
CALL r_sub  // D:=ad-bc
XCHG r0,s4  // b ; .. e D a f c d
MOV r1,s2   // f
CALL r_mul  // bf
POP r1      // d ; .. e D a f c
PUSH r0     //     .. e D a f c bf
MOV r0,s5   // e
CALL r_mul  // ed
POP r1      // bf; .. e D a f c
CALL r_sub  // Dx:=ed-bf
XCHG r0,s4  // e ; .. Dx D a f c
POP r1      // c ; .. Dx D a f
CALL r_mul  // ec
XCHG r0,s1  // a ; .. Dx D ec f
POP r1      // f ; .. Dx D ec
CALL r_mul  // af
POP r1      // ec; .. Dx D
CALL r_sub  // Dy:=af-ec
XCHG r0,s1  // Dx; .. Dy D
MOV r1,s0   // D
CALL r_div  // x:=Dx/D
XCHG r0,s1  // Dy; .. x D
POP r1      // D ; .. x
CALL r_div  // y:=Dy/D
MOV r1,r0   // y
POP r0      // x ; ..
RET
We have used 41 instructions: 17 one-byte (eight PUSH\texttt{PUSH}/POP\texttt{POP} pairs and one RET\texttt{RET}), 13 two-byte (MOV\texttt{MOV} and XCHG\texttt{XCHG}; out of them 11 “new” ones, involving the stack), and 11 three-byte (CALL\texttt{CALL}), for a total of 171+132+113=7617\cdot1+13\cdot2+11\cdot3=76 bytes.32

C.3.3. Three-address and two-address register machines, m=8m=8 preserved registers

Now we have eight registers, r8\texttt{r8} to r15\texttt{r15}, that are preserved by subroutine calls. We might keep some intermediate values there instead of pushing them into the stack. However, the penalty for doing so consists in a PUSH\texttt{PUSH}/POP\texttt{POP} pair for every such register that we choose to use, because our function is also required to preserve its original value. It seems that using these registers under such a penalty does not improve the density of the code, so the optimal code for three- and two-address machines for m=8m=8 preserved registers is the same as that provided in C.3.2, with a total of 42 instructions and 74 code bytes.

C.3.4. Three-address and two-address register machines, m=16m=16 preserved registers

This time all registers must be preserved by the subroutines, excluding those used for returning the results. This means that our code must preserve the original values of r2\texttt{r2} to r5\texttt{r5}, as well as any other registers it uses for temporary values. A straightforward way of writing the code of our subroutine would be to push registers r2\texttt{r2} up to, say, r8\texttt{r8} into the stack, then perform all the operations required, using r6\texttt{r6}r8\texttt{r8} for intermediate values, and finally restore registers from the stack. However, this would not optimize code size. We choose another approach:
PUSH r0     // STACK: a
PUSH r1     // STACK: a b
MOV r0,r1   // b
MOV r1,r2   // c
CALL r_mul  // bc
PUSH r0     //     .. a b bc
MOV r0,s2   // a
MOV r1,r3   // d
CALL r_mul  // ad
POP r1      // bc; .. a b
CALL r_sub  // D:=ad-bc
XCHG r0,s0  // b;  .. a D
MOV r1,r5   // f
CALL r_mul  // bf
PUSH r0     //     .. a D bf
MOV r0,r4   // e
MOV r1,r3   // d
CALL r_mul  // ed
POP r1      // bf; .. a D
CALL r_sub  // Dx:=ed-bf
XCHG r0,s1  // a ; .. Dx D
MOV r1,r5   // f
CALL r_mul  // af
PUSH r0     //     .. Dx D af
MOV r0,r4   // e
MOV r1,r2   // c
CALL r_mul  // ec
MOV r1,r0   // ec
POP r0      // af; .. Dx D
CALL r_sub  // Dy:=af-ec
XCHG r0,s1  // Dx; .. Dy D
MOV r1,s0   // D
CALL r_div  // x:=Dx/D
XCHG r0,s1  // Dy; .. x D
POP r1      // D ; .. x
CALL r_div  // y:=Dy/D
MOV r1,r0   // y
POP r0      // x
RET
We have used 39 instructions: 11 one-byte, 17 two-byte (among them 5 “new” instructions), and 11 three-byte, for a total of 111+172+113=7811\cdot1+17\cdot2+11\cdot3=78 bytes. Somewhat paradoxically, the code size in bytes is slightly longer than in the previous case (cf. C.3.2), contrary to what one might have expected. This is partially due to the fact that we have assumed two-byte encodings for “new” MOV\texttt{MOV} and XCHG\texttt{XCHG} instructions involving the stack, similarly to the “old” instructions. Most existing architectures (such as x86-64) use longer encodings (maybe even twice as long) for their counterparts of our “new” move and exchange instructions compared to the “usual” register-register ones. Taking this into account, we see that we would have obtained here 83 bytes (versus 87 for the code in C.3.2) assuming three-byte encodings of new operations, and 88 bytes (versus 98) assuming four-byte encodings. This shows that, for two-address architectures without optimized encodings for register-stack move and exchange operations, m=16m=16 preserved registers might result in slightly shorter code for some non-leaf functions, at the expense of leaf functions (cf. C.2.3 and C.2.4), which would become considerably longer.

C.3.5. One-address register machine, m=0m=0 preserved registers

For our one-address register machine, we assume that new register-stack instructions work through the accumulator only. Therefore, we have three new instructions, LD s(j)\texttt{LD s}(j) (equivalent to MOV r0,s(j)\texttt{MOV r0,s}(j) of two-address machines), ST s(j)\texttt{ST s}(j) (equivalent to MOV s(j),r0\texttt{MOV s}(j)\texttt{,r0}), and XCHG s(j)\texttt{XCHG s}(j) (equivalent to XCHG r0,s(j)\texttt{XCHG r0,s}(j)). To make the comparison with two-address machines more interesting, we assume one-byte encodings for these new instructions, even though this would consume 48/256=3/1648/256=3/16 of the opcode space. By adapting the code provided in C.3.2 to the one-address machine, we obtain the following:
PUSH r4     // STACK: e
PUSH r1     // STACK: e b
PUSH r0     //     .. e b a
PUSH r6     //     .. e b a f
PUSH r2     //     .. e b a f c
PUSH r3     //     .. e b a f c d
LD s1       // r0:=c
XCHG r1     // r0:=b, r1:=c
CALL r_mul  // bc
PUSH r0     //     .. e b a f c d bc
LD s1       // d
XCHG r1     // r1:=d
LD s4       // a
CALL r_mul  // ad
POP r1      // bc; .. e b a f c d
CALL r_sub  // D:=ad-bc
XCHG s4     // b ; .. e D a f c d
XCHG r1
LD s2       // f
XCHG r1     // r0:=b, r1:=f
CALL r_mul  // bf
POP r1      // d ; .. e D a f c
PUSH r0     //     .. e D a f c bf
LD s5       // e
CALL r_mul  // ed
POP r1      // bf; .. e D a f c
CALL r_sub  // Dx:=ed-bf
XCHG s4     // e ; .. Dx D a f c
POP r1      // c ; .. Dx D a f
CALL r_mul  // ec
XCHG s1     // a ; .. Dx D ec f
POP r1      // f ; .. Dx D ec
CALL r_mul  // af
POP r1      // ec; .. Dx D
CALL r_sub  // Dy:=af-ec
XCHG s1     // Dx; .. Dy D
POP r1      // D ; .. Dy
PUSH r1     //     .. Dy D
CALL r_div  // x:=Dx/D
XCHG s1     // Dy; .. x D
POP r1      // D ; .. x
CALL r_div  // y:=Dy/D
XCHG r1     // r1:=y
POP r0      // r0:=x ; ..
RET
We have used 45 instructions: 34 one-byte and 11 three-byte, for a total of 67 bytes. Compared to the 76 bytes used by two- and three-address machines in C.3.2, we see that, again, the one-address register machine code may be denser than that of two-register machines, at the expense of utilizing more opcode space (just as shown in C.2). However, this time the extra 3/16 of the opcode space was used for data manipulation instructions, which do not depend on specific arithmetic operations or user functions invoked.

C.3.6. One-address register machine, m=8m=8 preserved registers

As explained in C.3.3, the preservation of r8\texttt{r8}r15\texttt{r15} between subroutine calls does not improve the size of our previously written code, so the one-address machine will use for m=8m=8 the same code provided in C.3.5.

C.3.7. One-address register machine, m=16m=16 preserved registers

We simply adapt the code provided in C.3.4 to the one-address register machine:
PUSH r0     // STACK: a
PUSH r1     // STACK: a b
MOV r0,r1   // b
MOV r1,r2   // c
CALL r_mul  // bc
PUSH r0     //     .. a b bc
LD s2       // a
MOV r1,r3   // d
CALL r_mul  // ad
POP r1      // bc; .. a b
CALL r_sub  // D:=ad-bc
XCHG s0     // b;  .. a D
MOV r1,r5   // f
CALL r_mul  // bf
PUSH r0     //     .. a D bf
MOV r0,r4   // e
MOV r1,r3   // d
CALL r_mul  // ed
POP r1      // bf; .. a D
CALL r_sub  // Dx:=ed-bf
XCHG s1     // a ; .. Dx D
MOV r1,r5   // f
CALL r_mul  // af
PUSH r0     //     .. Dx D af
MOV r0,r4   // e
MOV r1,r2   // c
CALL r_mul  // ec
MOV r1,r0   // ec
POP r0      // af; .. Dx D
CALL r_sub  // Dy:=af-ec
XCHG s1     // Dx; .. Dy D
POP r1      // D ; .. Dy
PUSH r1     //     .. Dy D
CALL r_div  // x:=Dx/D
XCHG s1     // Dy; .. x D
POP r1      // D ; .. x
CALL r_div  // y:=Dy/D
MOV r1,r0   // y
POP r0      // x
RET
We have used 40 instructions: 18 one-byte, 11 two-byte, and 11 three-byte, for a total of 181+112+113=7318\cdot1+11\cdot2+11\cdot3=73 bytes.

C.3.8. Stack machine with basic stack primitives

We reuse the code provided in C.1.5, simply replacing arithmetic primitives (VM instructions) with subroutine calls. The only substantive modification is the insertion of the previously optional XCHG s1\texttt{XCHG s1} before the third multiplication, because even an optimizing compiler cannot now know whether CALL r_mul\texttt{CALL r\_mul} is a commutative operation. We have also used the “tail recursion optimization” by replacing the final CALL r_div\texttt{CALL r\_div} followed by RET\texttt{RET} with JMP r_div\texttt{JMP r\_div}.
PUSH s5     // a b c d e f a
PUSH s3     // a b c d e f a d
CALL r_mul  // a b c d e f ad
PUSH s5     // a b c d e f ad b
PUSH s5     // a b c d e f ad b c
CALL r_mul  // a b c d e f ad bc
CALL r_sub  // a b c d e f ad-bc
XCHG s3     // a b c ad-bc e f d
PUSH s2     // a b c ad-bc e f d e
XCHG s1     // a b c ad-bc e f e d
CALL r_mul  // a b c ad-bc e f ed
XCHG s5     // a ed c ad-bc e f b
PUSH s1     // a ed c ad-bc e f b f
CALL r_mul  // a ed c ad-bc e f bf
XCHG s1,s5  // a f c ad-bc e ed bf
CALL r_sub  // a f c ad-bc e ed-bf
XCHG s3     // a f ed-bf ad-bc e c
CALL r_mul  // a f ed-bf ad-bc ec
XCHG s3     // a ec ed-bf ad-bc f
XCHG s1,s4  // ad-bc ec ed-bf a f
CALL r_mul  // D ec Dx af
XCHG s1     // D ec af Dx
XCHG s2     // D Dx af ec
CALL r_sub  // D Dx Dy
XCHG s1     // D Dy Dx
PUSH s2     // D Dy Dx D
CALL r_div  // D Dy x
XCHG s2     // x Dy D
JMP r_div   // x y
We have used 29 instructions; assuming one-byte encodings for all stack operations, and three-byte encodings for CALL\texttt{CALL} and JMP\texttt{JMP} instructions, we end up with 51 bytes.

C.3.9. Stack machine with compound stack primitives

We again reuse the code provided in C.1.7, replacing arithmetic primitives with subroutine calls and making the tail recursion optimization:
PUSH2 s5,s2    // a b c d e f a d
CALL r_mul     // a b c d e f ad
PUSH2 s5,s4    // a b c d e f ad b c
CALL r_mul     // a b c d e f ad bc
CALL r_sub     // a b c d e f ad-bc
PUXC s2,s3     // a b c ad-bc e f e d
CALL r_mul     // a b c D e f ed
XCHG3 s6,s0,s5 //  (same as XCHG s2,s6; XCHG s1,s0; XCHG s0,s5)
               // e f c D a ed b
PUSH s5        // e f c D a ed b f
CALL r_mul     // e f c D a ed bf
CALL r_sub     // e f c D a ed-bf
XCHG s4        // e Dx c D a f
CALL r_mul     // e Dx c D af
XCHG2 s4,s2    // D Dx af e c
CALL r_mul     // D Dx af ec
CALL r_sub     // D Dx Dy
XCPU s1,s2     // D Dy Dx D
CALL r_div     // D Dy x
XCHG s2        // x Dy D
JMP r_div      // x y
This code uses only 20 instructions, 9 stack-related and 11 control flow-related (CALL\texttt{CALL} and JMP\texttt{JMP}), for a total of 48 bytes.

C.4 Comparison of machine code for sample non-leaf function

Table 5 summarizes the properties of machine code corresponding to the same source file provided in C.3.1. We consider only the “realistically” encoded three-address machines. Three-address and two-address machines have the same code density properties, but differ in the utilization of opcode space. The one-address machine, somewhat surprisingly, managed to produced shorter code than the two-address and three-address machines, at the expense of using up more than half of all opcode space. The stack machine is the obvious winner in this code density contest, without compromizing its excellent extendability (measured in opcode space used for arithmetic and other data transformation instructions).
MachinemmOperationsCode bytesOpcode space
datacont.totaldatacont.totaldataarithtotal
3-addr.0,829124142347635/25634/25672/256
16271239443478
2-addr.0,829124142347637/2564/25644/256
16271239443478
1-addr.0,833124533346797/25664/256164/256
16281240393473
stack (basic)-18112918335164/2564/25671/256
stack (comp.)-9112015334884/2564/25691/256
Table 5: A summary of machine code properties for hypothetical 3-address, 2-address, 1-address, and stack machines, generated for a sample non-leaf function (cf. C.3.1), assuming mm of the 16 registers must be preserved by called subroutines.

C.4.1. Combining with results for leaf functions

It is instructive to compare this table with the results in C.2 for a sample leaf function, summarized in Table 1 (for m=0m=0 preserved registers) and the very similar Table 3 (for m=8m=8 preserved registers), and, if one is still interested in case m=16m=16 (which turned out to be worse than m=8m=8 in almost all situations), also to Table 2. We see that the stack machine beats all register machines on non-leaf functions. As for the leaf functions, only the three-address machine with the “optimistic” encoding of arithmetic instructions was able to beat the stack machine, winning by 15%, by compromising its extendability. However, the same three-address machine produces 25% longer code for non-leaf functions. If a typical program consists of a mixture of leaf and non-leaf functions in approximately equal proportion, then the stack machine will still win.

C.4.2. A fairer comparison using a binary code instead of a byte code

Similarly to C.2.5, we may offer a fairer comparison of different register machines and the stack machine by using arbitrary binary codes instead of byte codes to encode instructions, and matching the opcode space used for data manipulation and arithmetic instructions by different machines. The results of this modified comparison are summarized in Table 6. We see that the stack machines still win by a large margin, while using less opcode space for stack/data manipulation.
MachinemmOperationsCode bytesOpcode space
datacont.totaldatacont.totaldataarithtotal
3-addr.0,829124135.53469.5110/2564/256117/256
1627123935.53469.5
2-addr.0,829124135.53469.5110/2564/256117/256
1627123935.53469.5
1-addr.0,8331245333467112/2564/256119/256
1628124033.53467.5
stack (basic)-18112918335164/2564/25671/256
stack (comp.)-9112015334884/2564/25691/256
Table 6: A summary of machine code properties for hypothetical 3-address, 2-address, 1-address, and stack machines, generated for a sample non-leaf function (cf. C.3.1), assuming mm of the 16 registers must be preserved by called subroutines. This time we use fractions of bytes to encode instructions, enabling a fairer comparison. Otherwise, this table is similar to Table 5.

C.4.3. Comparison with real machines

Note that our hypothetical register machines have been considerably optimized to produce shorter code than actually existing register machines; the latter are subject to other design considerations apart from code density and extendability, such as backward compatibility, faster instruction decoding, parallel execution of neighboring instructions, ease of automatically producing optimized code by compilers, and so on. For example, the very popular two-address register architecture x86-64 produces code that is approximately twice as long as our “ideal” results for the two-address machines. On the other hand, our results for the stack machines are directly applicable to TVM, which has been explicitly designed with the considerations presented in this appendix in mind. Furthermore, the actual TVM code is even shorter (in bytes) than shown in Table 5 because of the presence of the two-byte CALL\texttt{CALL} instruction, allowing TVM to call up to 256 user-defined functions from the dictionary at c3\texttt{c3}. This means that one should subtract 10 bytes from the results for stack machines in Table 5 if one wants to specifically consider TVM, rather than an abstract stack machine; this produces a code size of approximately 40 bytes (or shorter), almost half that of an abstract two-address or three-address machine.

C.4.4. Automatic generation of optimized code

An interesting point is that the stack machine code in our samples might have been generated automatically by a very simple optimizing compiler, which rearranges values near the top of the stack appropriately before invoking each primitive or calling a function as explained in 2.2.2 and 2.2.5. The only exception is the unimportant “manual” XCHG3\texttt{XCHG3} optimization described in C.1.7, which enabled us to shorten the code by one more byte. By contrast, the heavily optimized (with respect to size) code for register machines shown in C.3.2 and C.3.3 is unlikely to be produced automatically by an optimizing compiler. Therefore, if we had compared compiler-generated code instead of manually-generated code, the advantages of stack machines with respect to code density would have been even more striking.

References

1 N. Durov, Telegram Open Network, 2017.

Footnotes

[1] For example, there are no floating-point arithmetic operations (which could be efficiently implemented using hardware-supported double type on most modern CPUs) present in TVM, because the result of performing such operations is dependent on the specific underlying hardware implementation and rounding mode settings. Instead, TVM supports special integer arithmetic operations, which can be used to simulate fixed-point arithmetic if needed. Back ↑ 2 The production version will likely require some tweaks and modifications prior to launch, which will become apparent only after using the experimental version in the test environment for some time. Back ↑ 3 A high-level smart-contract language might create a visibility of variables for the ease of programming; however, the high-level source code working with variables will be translated into TVM machine code keeping all the values of these variables in the TVM stack. Back ↑ 4 In the TON Blockchain context, c7 is initialized with a singleton Tuple, the only component of which is a Tuple containing blockchain-specific data. The smart contract is free to modify c7 to store its temporary data provided the first component of this Tuple remains intact. Back ↑ 5 Strictly speaking, there is also the current library context, which consists of a dictionary with 256-bit keys and cell values, used to load library reference cells of 3.1.7. Back ↑ 6 Our inclusion of r0 here creates a minor conflict with our assumption that the accumulator register, if present, is also r0; for simplicity, we will resolve this problem by assuming that the first argument to a function is passed in the accumulator. Back ↑ 7 For instance, if one writes a function for extracting square roots, this function will always accept its argument and return its result in the same registers, in contrast with a hypothetical built-in square root instruction, which could allow the programmer to arbitrarily choose the source and destination registers. Therefore, a user-defined function is tremendously less flexible than a built-in instruction on a register machine. Back ↑ 8 Of course, if the second option is used, this will destroy the original arrangement of xx in the top of the stack. In this case, one should either issue a SWAP before XCHG s(j'), or replace the previous operation XCHG s(i) with XCHG s1, s(i), so that xx is exchanged with s1 from the beginning. Back ↑ 9 Notice that the most common XCHG s(i) operation is not really required here if we do not insist on keeping the same temporary value or variable always in the same stack location, but rather keep track of its subsequent locations. We will move it to some other location while preparing the arguments to the next primitive or function call. Back ↑ 10 An alternative, arguably better, translation of PUOO' s(i_1),…,s(i_γ) consists of the translation of OO' s(i_2),…,s(i_γ), followed by PUSH s(i_1+α-1); XCHG s(γ-1). Back ↑ 11 From the perspective of low-level cell operations, these data bits and cell references are not intermixed. In other words, an (ordinary) cell essentially is a couple consisting of a list of up to 1023 bits and of a list of up to four cell references, without prescribing an order in which the references and the data bits should be deserialized, even though TL-B schemes appear to suggest such an order. Back ↑ 12 From a theoretical perspective, we might say that a cell cc has an infinite sequence of hashes (Hashi(c))i1(\text{Hash}_i(c))_{i\geq1}, which eventually stabilizes: Hashi(c)Hash(c)\text{Hash}_i(c)\to\text{Hash}_\infty(c). Then the level ll is simply the largest index ii, such that Hashi(c)Hash(c)\text{Hash}_i(c)\neq\text{Hash}_\infty(c). Back ↑ 13 A pruned branch cell cc' of level ll is bound by a Merkle (proof or update) cell cc if there are exactly ll Merkle cells on the path from cc to its descendant cc', including cc. Back ↑ 14 Negative numbers are represented using two’s complement. For instance, integer 17-17 is serialized by instruction STI 8 into bitstring xEF. Back ↑ 15 A description of an older version of TL may be found at https://core.telegram.org/mtproto/TL. Back ↑ 16 The field’s name is useful for representing values of the type being defined in human-readable form, but it does not affect the binary serialization. Back ↑ 17 This is the “linear negation” operation ()(-)^\perp of linear logic, hence our notation ~. Back ↑ 18 In fact, ff may receive mm extra arguments and return mm modified values, which are passed to the next invocation of ff. This may be used to implement “map” and “reduce” operations with dictionaries. Back ↑ 19 Versions of this operation may be introduced where ff and gg receive an additional bitstring argument, equal to the key (for leaves) or to the common prefix of all keys (for forks) in the corresponding subtree. Back ↑ 20 If there are no bits of data left in code, but there is still exactly one reference, an implicit JMP to the cell at that reference is performed instead of an implicit RET. Back ↑ 21 Technically, TVM may simply invoke a virtual method run() of the continuation currently in cc. Back ↑ 22 The already used savelist cc.save of the new cc is emptied before the execution starts. Back ↑ 23 The implementation of REPEAT involves an extraordinary continuation that remembers the remaining number of iterations, the body of the loop cc, and the return continuation cc'. (The latter term represents the remainder of the body of the function that invoked REPEAT, which would be normally stored in c0 of the new cc.) Back ↑ 24 An important point here is that the tree of cells representing a TVM program cannot have cyclic references, so using CALLREF along with a reference to a cell higher up the tree would not work. Back ↑ 25 This is not exactly true. A more precise statement is that usually the codepage of the newly-created continuation is a known function of the current codepage. Back ↑ 26 This is another important mechanism of backward compatibility. All values of newly-added types, as well as values belonging to extended original types that do not belong to the original types (e.g., 513-bit integers that do not fit into 257 bits in the example above), are treated by all instructions (except stack manipulation instructions, which are naturally polymorphic, cf. 2.3.3) in the old codepages as “values of incorrect type”, and generate type-checking exceptions accordingly. Back ↑ 27 If the cell dumps are hexadecimal, encodings consisting of an integral number of hexadecimal digits (i.e., having length divisible by four bits) might be equally convenient. Back ↑ 28 Notice that it is the probability of occurrence in the code that counts, not the probability of being executed. An instruction occurring in the body of a loop executed a million times is still counted only once. Back ↑ 29 Notice that any modifications after launch cannot be done unilaterally; rather they would require the support of at least two-thirds of validators. Back ↑ 30 The preliminary version of TVM does not use codepage -2 for this purpose. This may change in the future. Back ↑ 31 It is interesting to compare this code with that generated by optimizing C compilers for the x86-64 architecture. First of all, the integer division operation for x86-64 uses the one-address form, with the (double-length) dividend to be supplied in accumulator pair r2:r0. The quotient is also returned in r0. As a consequence, two single-to-double extension operations (CDQ or CQO) and at least one move operation need to be added. Secondly, the encoding used for arithmetic and move operations is less optimistic than in our example above, requiring about three bytes per operation on average. As a result, we obtain a total of 43 bytes for 32-bit integers, and 68 bytes for 64-bit integers. Back ↑ 32 Code produced for this function by an optimizing compiler for x86-64 architecture with size-optimization enabled actually occupied 150 bytes, due mostly to the fact that actual instruction encodings are about twice as long as we had optimistically assumed. Back ↑