Skip to main content
Functions in Tolk can be defined using assembler code. It’s a low-level feature that requires understanding of stack layout, Fift, and TVM.

Standard functions

Standard functions are asm wrappers. Many functions from the standard library are translated to the Fift assembler directly. For example, TVM has a HASHCU instruction, which is “calculate hash of a cell”. It pops a cell from the stack and pushes an integer in the range 0 to 2256-1. Therefore, the method cell.hash is defined:
The type system guarantees that when this method is invoked, a TVM CELL will be the topmost element (self).

Custom functions

Custom functions are declared in the same way. A call incThenNegate(10) is translated into those commands. Specify @pure if the body does not modify TVM state or throw exceptions. The return type for asm functions is mandatory. For regular functions, it’s inferred from return statements.

Multi-line asm

To embed a multi-line command, use triple quotes:
It is treated as a single string and inserted as-is into Fift output. It can contain // comments valid for Fift.

Stack order for multiple slots

When calling a function, arguments are pushed in the declared order. The last parameter becomes the topmost stack element. If an instruction produces several slots, the resulting type should be a tensor or a struct. For example, write a function abs2 that calculates abs() for two values at once: abs2(-5, -10) = (5, 10). The comments show the stack layout for each step. The rightmost value represents the top of the stack.

Stack-based argument reordering

Sometimes a function accepts parameters in an order different from what a TVM instruction expects. For example, GETSTORAGEFEE expects the parameters in the order cells, bits, seconds, and workchain. For a clearer API, the function should take the workchain as its first argument. To reorder stack positions, use the asm(<INPUT_ORDER>) syntax:
Similarly for return values. If multiple slots are returned and must be reordered to match typing, use the asm(-> <RETURN_ORDER>) syntax:
Both the input and output sides can be combined: asm(<INPUT_ORDER> -> <RETURN_ORDER>). Reordering is mostly used with mutate variables.

mutate and self in assembler functions

The mutate keyword, which makes a parameter mutable, implicitly returns updated values through the stack in both regular and asm functions. Consider regular functions first. The compiler applies all transformations automatically.
To implement increment() using asm:
The function returns type void. The type system treats it as returning no value. However, INC leaves a number on the stack — that’s a hidden “return x” from a manual implementation. Similarly, it works for mutate self. An asm function should place newSelf on the stack before the actual result:
To return self for chaining, specify a return type:

asm is compatible with structures

Methods on structures can be declared in asm when their field layout is known. Fields are placed sequentially. For example, a structure with a single field is equivalent to that field.
Structures can also be used instead of tensors as return types. It appears in map<K, V> methods on TVM dictionaries:

Generics in asm should be single-slot

Consider tuple.push. The TPUSH instruction pops (tuple, someVal) and pushes (newTuple). It works with any T that occupies a single stack slot, such as int, int8, or slice.
How does t.push(somePoint) work? It does not compile, because Point { x, y } occupies two stack slots rather than one, which breaks the expected the stack.
Only regular and built-in generics support variadic type arguments. asm do not.

Do not use asm for micro-optimizations

Use asm only for rarely used TVM instructions that are not covered by the standard library, such as manual merkle-proof parsing or extended hash calculations. Using asm for micro-optimizations is discouraged. The compiler already produces bitcode from clear, structured logic. For example, it automatically inlines simple functions, so one-line helper methods do not add gas overhead.
A manual 32 STU sequence provides no advantage in this case. The compiler:
  • inlines the function;
  • merges constant flags with subsequent stores into STSLICECONST.