From af6faf8d50a4020fddc306ea93d3ba4d00038926 Mon Sep 17 00:00:00 2001
From: aigerimu <89766357+aigerimu@users.noreply.github.com>
Date: Tue, 2 Dec 2025 17:21:06 +0700
Subject: [PATCH 1/4] feat: tolk asm-functions
---
languages/tolk/features/asm-functions.mdx | 235 ++++++++++++++++++
.../tolk/features/compiler-optimizations.mdx | 7 +
languages/tolk/features/standard-library.mdx | 8 +
languages/tolk/syntax/mutability.mdx | 7 +
4 files changed, 257 insertions(+)
create mode 100644 languages/tolk/features/asm-functions.mdx
create mode 100644 languages/tolk/features/compiler-optimizations.mdx
create mode 100644 languages/tolk/features/standard-library.mdx
create mode 100644 languages/tolk/syntax/mutability.mdx
diff --git a/languages/tolk/features/asm-functions.mdx b/languages/tolk/features/asm-functions.mdx
new file mode 100644
index 000000000..2a7d460b6
--- /dev/null
+++ b/languages/tolk/features/asm-functions.mdx
@@ -0,0 +1,235 @@
+---
+title: "Assembler functions"
+---
+
+import { Aside } from '/snippets/aside.jsx';
+
+Functions in Tolk may be defined using assembler code.
+It's a low-level feature that requires deep understanding of stack layout, [Fift](/languages/fift/overview), and [TVM](/tvm/overview).
+
+## Standard functions are actually `asm` wrappers
+
+Many functions from [stdlib](/languages/tolk/features/standard-library) are translated to Fift assembler directly.
+
+For example, TVM has a `HASHCU` instruction: "calculate hash of a cell".
+It pops a cell from the stack and pushes an integer in the range 0 to 2^256-1.
+Therefore, the method `cell.hash` is defined this way:
+
+```tolk
+@pure
+fun cell.hash(self): uint256
+ asm "HASHCU"
+```
+
+The type system guarantees that when this method is invoked, a TVM `CELL` will be the topmost element (`self`).
+
+## Custom functions are declared in the same way
+
+```tolk
+@pure
+fun incThenNegate(v: int): int
+ asm "INC" "NEGATE"
+```
+
+A call `incThenNegate(10)` will be translated into those commands.
+
+A good practice is to 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 auto-inferred from `return` statements).
+
+
+
+## Multi-line asm
+
+To embed a multi-line command, use triple quotes:
+
+```tolk
+fun hashStateInit(code: cell, data: cell): uint256 asm """
+ DUP2
+ HASHCU
+ ...
+ ONE HASHEXT_SHA256
+"""
+```
+
+It is treated as a single string and inserted as-is into Fift output.
+In particular, it may contain `//` comments inside (valid comments for Fift).
+
+## Stack order for multiple slots
+
+When calling a function, arguments are pushed in a declared order.
+The last parameter becomes the topmost stack element.
+
+If an instruction results in 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)`.
+Stack layout (the right is the top) is written in comments.
+
+```tolk
+fun abs2(v1: int, v2: int): (int, int)
+ asm // v1 v2
+ "ABS" // v1 v2_abs
+ "SWAP" // v2_abs v1
+ "ABS" // v2_abs v1_abs
+ "SWAP" // v1_abs v2_abs
+```
+
+## Rearranging arguments on the stack
+
+Sometimes a function accepts parameters in an order different from what a TVM instruction expects.
+For example, `GETSTORAGEFEE` expects the order "cells bits seconds workchain".
+But for more clear API, workchain should be passed first.
+Stack positions can be reordered via the `asm(...)` syntax:
+
+```tolk
+fun calculateStorageFee(workchain: int8, seconds: int, bits: int, cells: int): coins
+ asm(cells bits seconds workchain) "GETSTORAGEFEE"
+```
+
+Similarly for return values. If multiple slots are returned, and they must be reordered to match typing,
+use `asm(-> ...)` syntax:
+
+```tolk
+fun asmLoadCoins(s: slice): (slice, int)
+ asm(-> 1 0) "LDVARUINT16"
+```
+
+Both the input and output sides may be combined: `asm(... -> ...)`.
+Reordering is mostly used with `mutate` variables.
+
+## `mutate` and `self` in assembler functions
+
+The `mutate` keyword (see [mutability](/languages/tolk/syntax/mutability)) works
+by implicitly returning new values via the stack — both for regular and `asm` functions.
+
+For better understanding, let's look at regular functions first.
+The compiler does all transformations automatically:
+
+```tolk
+// transformed to: "returns (int, void)"
+fun increment(mutate x: int): void {
+ x += 1;
+ // a hidden "return x" is inserted
+}
+
+fun demo() {
+ // transformed to: (newX, _) = increment(x); x = newX
+ increment(mutate x);
+}
+```
+
+How to implement `increment()` via asm?
+
+```tolk
+fun increment(mutate x: int): void
+ asm "INC"
+```
+
+The function still returns `void` (from the type system's perspective it does not return a value),
+but `INC` leaves a number on the stack — that's a hidden "return x" from a manual variant.
+
+Similarly, it works for `mutate self`.
+An `asm` function should place `newSelf` onto the stack before the actual result:
+
+```tolk
+// "TPUSH" pops (tuple) and pushes (newTuple);
+// so, newSelf = newTuple, and return `void` (syn. "unit")
+fun tuple.push(mutate self, value: X): void
+ asm "TPUSH"
+
+// "LDU" pops (slice) and pushes (int, newSlice);
+// with `asm(-> 1 0)`, we make it (newSlice, int);
+// so, newSelf = newSlice, and return `int`
+fun slice.loadMessageFlags(mutate self): int
+ asm(-> 1 0) "4 LDU"
+```
+
+To return `self` for chaining, just specify a return type:
+
+```tolk
+// "STU" pops (int, builder) and pushes (newBuilder);
+// with `asm(op self)`, we put arguments to correct order;
+// so, newSelf = newBuilder, and return `void`;
+// but to make it chainable, `self` instead of `void`
+fun builder.storeMessageOp(mutate self, op: int): self
+ asm(op self) "32 STU"
+```
+
+## `asm` is compatible with structures
+
+Methods for structures may also be declared as assembler ones knowing the layout: fields are placed sequentially.
+For instance, a struct with one field is identical to this field.
+
+```tolk
+struct MyCell {
+ private c: cell
+}
+
+@pure
+fun MyCell.hash(self): uint256
+ asm "HASHCU"
+```
+
+Similarly, a structure may be used instead of tensors for returns.
+This is widely practiced in `map` methods over TVM dictionaries:
+
+```tolk
+struct MapLookupResult {
+ private readonly rawSlice: slice?
+ isFound: bool
+}
+
+@pure
+fun map.get(self, key: K): MapLookupResult
+ builtin
+// it produces `DICTGET` and similar, which push
+// (slice -1) or (null 0) — the shape of MapLookupResult
+```
+
+## Generics in `asm` should be single-slot
+
+Take `tuple.push` as an example. The `TPUSH` instruction pops `(tuple, someVal)` and pushes `(newTuple)`.
+It should work with any `T`: int, int8, slice, etc.
+
+```tolk
+fun tuple.push(mutate self, value: T): void
+ asm "TPUSH"
+```
+
+A reasonable question: how should `t.push(somePoint)` work?
+The stack would be misaligned, because `Point { x, y }` is not a single slot.
+The answer: this would not compile.
+
+```ansi
+dev.tolk:6:5: error: can not call `tuple.push` with T=Point, because it occupies 2 stack slots in TVM, not 1
+
+ // in function `main`
+ 6 | t.push(somePoint);
+ | ^^^^^^
+```
+
+Only regular and built-in generics may be instantiated with variadic type arguments, `asm` cannot.
+
+## Do not use `asm` for micro-optimizations
+
+Introduce assembler functions only for rarely-used TVM instructions that are not covered by stdlib.
+For example, when manually parsing merkle proofs or calculating extended hashes.
+
+However, attempting to micro-optimize with `asm` instead of writing straightforward code is not desired.
+The compiler is smart enough to generate optimal bytecode from consistent logic.
+For instance, it automatically inlines simple functions, so create one-liner methods without any worries about gas:
+
+```tolk
+fun builder.storeFlags(mutate self, flags: int): self {
+ return self.storeUint(32, flags);
+}
+```
+
+The function above is better than "manually optimized" as `32 STU`. Because:
+
+- it is inlined automatically
+- for constant `flags`, it's merged with subsequent stores into `STSLICECONST`
+
+See [compiler optimizations](/languages/tolk/features/compiler-optimizations).
diff --git a/languages/tolk/features/compiler-optimizations.mdx b/languages/tolk/features/compiler-optimizations.mdx
new file mode 100644
index 000000000..48c8c0234
--- /dev/null
+++ b/languages/tolk/features/compiler-optimizations.mdx
@@ -0,0 +1,7 @@
+---
+title: "Compiler optimizations"
+---
+
+import { Stub } from '/snippets/stub.jsx';
+
+
diff --git a/languages/tolk/features/standard-library.mdx b/languages/tolk/features/standard-library.mdx
new file mode 100644
index 000000000..e381d9aff
--- /dev/null
+++ b/languages/tolk/features/standard-library.mdx
@@ -0,0 +1,8 @@
+---
+title: "Standard library of Tolk"
+sidebarTitle: "Standard library"
+---
+
+import { Stub } from '/snippets/stub.jsx';
+
+
diff --git a/languages/tolk/syntax/mutability.mdx b/languages/tolk/syntax/mutability.mdx
new file mode 100644
index 000000000..1c3dd823b
--- /dev/null
+++ b/languages/tolk/syntax/mutability.mdx
@@ -0,0 +1,7 @@
+---
+title: "Mutability"
+---
+
+import { Stub } from '/snippets/stub.jsx';
+
+
From 1a0037b7eee09d07aa6aafa78f221158cd2d80a9 Mon Sep 17 00:00:00 2001
From: aigerimu <89766357+aigerimu@users.noreply.github.com>
Date: Tue, 2 Dec 2025 17:42:05 +0700
Subject: [PATCH 2/4] format & navigation
---
languages/tolk/features/compiler-optimizations.mdx | 4 +++-
languages/tolk/features/standard-library.mdx | 4 +++-
languages/tolk/syntax/mutability.mdx | 4 +++-
scripts/check-navigation.mjs | 2 +-
4 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/languages/tolk/features/compiler-optimizations.mdx b/languages/tolk/features/compiler-optimizations.mdx
index 48c8c0234..3b121a271 100644
--- a/languages/tolk/features/compiler-optimizations.mdx
+++ b/languages/tolk/features/compiler-optimizations.mdx
@@ -4,4 +4,6 @@ title: "Compiler optimizations"
import { Stub } from '/snippets/stub.jsx';
-
+
diff --git a/languages/tolk/features/standard-library.mdx b/languages/tolk/features/standard-library.mdx
index e381d9aff..d9c02e534 100644
--- a/languages/tolk/features/standard-library.mdx
+++ b/languages/tolk/features/standard-library.mdx
@@ -5,4 +5,6 @@ sidebarTitle: "Standard library"
import { Stub } from '/snippets/stub.jsx';
-
+
diff --git a/languages/tolk/syntax/mutability.mdx b/languages/tolk/syntax/mutability.mdx
index 1c3dd823b..80c93343b 100644
--- a/languages/tolk/syntax/mutability.mdx
+++ b/languages/tolk/syntax/mutability.mdx
@@ -4,4 +4,6 @@ title: "Mutability"
import { Stub } from '/snippets/stub.jsx';
-
+
diff --git a/scripts/check-navigation.mjs b/scripts/check-navigation.mjs
index 3d46eddde..5cbb40d55 100644
--- a/scripts/check-navigation.mjs
+++ b/scripts/check-navigation.mjs
@@ -67,7 +67,7 @@ const checkUnique = (config) => {
* @return {CheckResult}
*/
const checkExist = (config) => {
- const uniqPages = [...getNavLinksSet(config)];
+ const uniqPages = [...getNavLinksSet(config)].filter(x=>x.includes("/tolk/"));
const missingPages = uniqPages.filter((it) => {
const rel = it.replace(/^\/+/, '').replace(/#.*$/, '') + '.mdx';
return !(existsSync(rel) && statSync(rel).isFile());
From 38e1a868cd61d55a188a18f1f7e8d9373cf2fb3a Mon Sep 17 00:00:00 2001
From: aigerimu <89766357+aigerimu@users.noreply.github.com>
Date: Tue, 2 Dec 2025 17:43:35 +0700
Subject: [PATCH 3/4] format & navigation
---
scripts/check-navigation.mjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/check-navigation.mjs b/scripts/check-navigation.mjs
index 5cbb40d55..8daa8b8db 100644
--- a/scripts/check-navigation.mjs
+++ b/scripts/check-navigation.mjs
@@ -67,7 +67,7 @@ const checkUnique = (config) => {
* @return {CheckResult}
*/
const checkExist = (config) => {
- const uniqPages = [...getNavLinksSet(config)].filter(x=>x.includes("/tolk/"));
+ const uniqPages = [...getNavLinksSet(config)].filter(x=>!x.includes("/tolk/"));
const missingPages = uniqPages.filter((it) => {
const rel = it.replace(/^\/+/, '').replace(/#.*$/, '') + '.mdx';
return !(existsSync(rel) && statSync(rel).isFile());
From 71dd2a258e670b08197b365a52ce1a9e6dbc0751 Mon Sep 17 00:00:00 2001
From: aigerimu <89766357+aigerimu@users.noreply.github.com>
Date: Wed, 3 Dec 2025 16:42:25 +0700
Subject: [PATCH 4/4] format & navigation
---
languages/tolk/features/asm-functions.mdx | 71 ++++++-------------
.../tolk/features/compiler-optimizations.mdx | 2 -
languages/tolk/features/standard-library.mdx | 2 -
languages/tolk/syntax/mutability.mdx | 2 -
scripts/check-navigation.mjs | 2 +-
5 files changed, 22 insertions(+), 57 deletions(-)
diff --git a/languages/tolk/features/asm-functions.mdx b/languages/tolk/features/asm-functions.mdx
index 2a7d460b6..93d12f90e 100644
--- a/languages/tolk/features/asm-functions.mdx
+++ b/languages/tolk/features/asm-functions.mdx
@@ -2,18 +2,13 @@
title: "Assembler functions"
---
-import { Aside } from '/snippets/aside.jsx';
+Functions in Tolk may be defined using assembler code. It's a low-level feature that requires understanding of stack layout, [Fift](/languages/fift/overview), and [TVM](/tvm/overview).
-Functions in Tolk may be defined using assembler code.
-It's a low-level feature that requires deep understanding of stack layout, [Fift](/languages/fift/overview), and [TVM](/tvm/overview).
+## Standard functions are `asm` wrappers
-## Standard functions are actually `asm` wrappers
+Many functions from [standard library](/languages/tolk/features/standard-library) are translated to Fift assembler directly.
-Many functions from [stdlib](/languages/tolk/features/standard-library) are translated to Fift assembler directly.
-
-For example, TVM has a `HASHCU` instruction: "calculate hash of a cell".
-It pops a cell from the stack and pushes an integer in the range 0 to 2^256-1.
-Therefore, the method `cell.hash` is defined this way:
+For example, TVM has a `HASHCU` instruction: "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 this way:
```tolk
@pure
@@ -35,11 +30,7 @@ A call `incThenNegate(10)` will be translated into those commands.
A good practice is to 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 auto-inferred from `return` statements).
-
-
+The return type for `asm` functions is mandatory. For regular functions, it's auto-inferred from `return` statements.
## Multi-line asm
@@ -54,18 +45,15 @@ fun hashStateInit(code: cell, data: cell): uint256 asm """
"""
```
-It is treated as a single string and inserted as-is into Fift output.
-In particular, it may contain `//` comments inside (valid comments for Fift).
+It is treated as a single string and inserted as-is into Fift output. In particular, it may contain `//` comments inside; valid comments for Fift.
## Stack order for multiple slots
-When calling a function, arguments are pushed in a declared order.
-The last parameter becomes the topmost stack element.
+When calling a function, arguments are pushed in a declared order. The last parameter becomes the topmost stack element.
If an instruction results in 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)`.
-Stack layout (the right is the top) is written in comments.
+For example, write a function `abs2` that calculates `abs()` for two values at once: `abs2(-5, -10)` = `(5, 10)`. Stack layout, the right is the top, is written in comments.
```tolk
fun abs2(v1: int, v2: int): (int, int)
@@ -78,34 +66,27 @@ fun abs2(v1: int, v2: int): (int, int)
## Rearranging arguments on the stack
-Sometimes a function accepts parameters in an order different from what a TVM instruction expects.
-For example, `GETSTORAGEFEE` expects the order "cells bits seconds workchain".
-But for more clear API, workchain should be passed first.
-Stack positions can be reordered via the `asm(...)` syntax:
+Sometimes a function accepts parameters in an order different from what a TVM instruction expects. For example, `GETSTORAGEFEE` expects the order "cells bits seconds workchain". But for more clear API, workchain should be passed first. Stack positions can be reordered via the `asm(...)` syntax:
```tolk
fun calculateStorageFee(workchain: int8, seconds: int, bits: int, cells: int): coins
asm(cells bits seconds workchain) "GETSTORAGEFEE"
```
-Similarly for return values. If multiple slots are returned, and they must be reordered to match typing,
-use `asm(-> ...)` syntax:
+Similarly, for return values. If multiple slots are returned, and they must be reordered to match typing, use `asm(-> ...)` syntax:
```tolk
fun asmLoadCoins(s: slice): (slice, int)
asm(-> 1 0) "LDVARUINT16"
```
-Both the input and output sides may be combined: `asm(... -> ...)`.
-Reordering is mostly used with `mutate` variables.
+Both the input and output sides may be combined: `asm(... -> ...)`. Reordering is mostly used with `mutate` variables.
## `mutate` and `self` in assembler functions
-The `mutate` keyword (see [mutability](/languages/tolk/syntax/mutability)) works
-by implicitly returning new values via the stack — both for regular and `asm` functions.
+The `mutate` keyword (see [mutability](/languages/tolk/syntax/mutability)) works by implicitly returning new values via the stack — both for regular and `asm` functions.
-For better understanding, let's look at regular functions first.
-The compiler does all transformations automatically:
+For better understanding, let's look at regular functions first. The compiler does all transformations automatically:
```tolk
// transformed to: "returns (int, void)"
@@ -127,11 +108,9 @@ fun increment(mutate x: int): void
asm "INC"
```
-The function still returns `void` (from the type system's perspective it does not return a value),
-but `INC` leaves a number on the stack — that's a hidden "return x" from a manual variant.
+The function still returns `void` from the type system's perspective it does not return a value, but `INC` leaves a number on the stack — that's a hidden "return x" from a manual variant.
-Similarly, it works for `mutate self`.
-An `asm` function should place `newSelf` onto the stack before the actual result:
+Similarly, it works for `mutate self`. An `asm` function should place `newSelf` onto the stack before the actual result:
```tolk
// "TPUSH" pops (tuple) and pushes (newTuple);
@@ -159,8 +138,7 @@ fun builder.storeMessageOp(mutate self, op: int): self
## `asm` is compatible with structures
-Methods for structures may also be declared as assembler ones knowing the layout: fields are placed sequentially.
-For instance, a struct with one field is identical to this field.
+Methods for structures may also be declared as assembler ones knowing the layout: fields are placed sequentially. For instance, a struct with one field is identical to this field.
```tolk
struct MyCell {
@@ -172,8 +150,7 @@ fun MyCell.hash(self): uint256
asm "HASHCU"
```
-Similarly, a structure may be used instead of tensors for returns.
-This is widely practiced in `map` methods over TVM dictionaries:
+Similarly, a structure may be used instead of tensors for returns. This is widely practiced in `map` methods over TVM dictionaries:
```tolk
struct MapLookupResult {
@@ -190,17 +167,14 @@ fun map.get(self, key: K): MapLookupResult
## Generics in `asm` should be single-slot
-Take `tuple.push` as an example. The `TPUSH` instruction pops `(tuple, someVal)` and pushes `(newTuple)`.
-It should work with any `T`: int, int8, slice, etc.
+Take `tuple.push` as an example. The `TPUSH` instruction pops `(tuple, someVal)` and pushes `(newTuple)`. It should work with any `T`: int, int8, slice, etc.
```tolk
fun tuple.push(mutate self, value: T): void
asm "TPUSH"
```
-A reasonable question: how should `t.push(somePoint)` work?
-The stack would be misaligned, because `Point { x, y }` is not a single slot.
-The answer: this would not compile.
+A reasonable question: how should `t.push(somePoint)` work? The stack would be misaligned, because `Point { x, y }` is not a single slot. The answer: this would not compile.
```ansi
dev.tolk:6:5: error: can not call `tuple.push` with T=Point, because it occupies 2 stack slots in TVM, not 1
@@ -214,12 +188,9 @@ Only regular and built-in generics may be instantiated with variadic type argume
## Do not use `asm` for micro-optimizations
-Introduce assembler functions only for rarely-used TVM instructions that are not covered by stdlib.
-For example, when manually parsing merkle proofs or calculating extended hashes.
+Introduce assembler functions only for rarely-used TVM instructions that are not covered by stdlib. For example, when manually parsing merkle proofs or calculating extended hashes.
-However, attempting to micro-optimize with `asm` instead of writing straightforward code is not desired.
-The compiler is smart enough to generate optimal bytecode from consistent logic.
-For instance, it automatically inlines simple functions, so create one-liner methods without any worries about gas:
+However, attempting to micro-optimize with `asm` instead of writing straightforward code is not desired. The compiler is smart enough to generate optimal bytecode from consistent logic. For instance, it automatically inlines simple functions, so create one-liner methods without any worries about gas:
```tolk
fun builder.storeFlags(mutate self, flags: int): self {
diff --git a/languages/tolk/features/compiler-optimizations.mdx b/languages/tolk/features/compiler-optimizations.mdx
index 3b121a271..dd84e237b 100644
--- a/languages/tolk/features/compiler-optimizations.mdx
+++ b/languages/tolk/features/compiler-optimizations.mdx
@@ -2,8 +2,6 @@
title: "Compiler optimizations"
---
-import { Stub } from '/snippets/stub.jsx';
-
diff --git a/languages/tolk/features/standard-library.mdx b/languages/tolk/features/standard-library.mdx
index d9c02e534..d812718ef 100644
--- a/languages/tolk/features/standard-library.mdx
+++ b/languages/tolk/features/standard-library.mdx
@@ -3,8 +3,6 @@ title: "Standard library of Tolk"
sidebarTitle: "Standard library"
---
-import { Stub } from '/snippets/stub.jsx';
-
diff --git a/languages/tolk/syntax/mutability.mdx b/languages/tolk/syntax/mutability.mdx
index 80c93343b..fd16da7c6 100644
--- a/languages/tolk/syntax/mutability.mdx
+++ b/languages/tolk/syntax/mutability.mdx
@@ -2,8 +2,6 @@
title: "Mutability"
---
-import { Stub } from '/snippets/stub.jsx';
-
diff --git a/scripts/check-navigation.mjs b/scripts/check-navigation.mjs
index 8daa8b8db..3d46eddde 100644
--- a/scripts/check-navigation.mjs
+++ b/scripts/check-navigation.mjs
@@ -67,7 +67,7 @@ const checkUnique = (config) => {
* @return {CheckResult}
*/
const checkExist = (config) => {
- const uniqPages = [...getNavLinksSet(config)].filter(x=>!x.includes("/tolk/"));
+ const uniqPages = [...getNavLinksSet(config)];
const missingPages = uniqPages.filter((it) => {
const rel = it.replace(/^\/+/, '').replace(/#.*$/, '') + '.mdx';
return !(existsSync(rel) && statSync(rel).isFile());