Assembler function definition
In FunC, functions can be defined directly using assembler code. This is done by declaring the function body using theasm
keyword,
followed by one or more assembler commands written inside double quotes "
, and finalizing with the symbol ;
.
For example, the following function increments an integer and then negates it:
inc_then_negate
are translated to 2 assembler commands INC
and NEGATE
.
Alternatively, the function can be written as:
INC NEGATE
is treated as a single assembler command by FunC, but the Fift assembler correctly interprets it as two separate commands.
The list of assembler commands can be found here: TVM instructions.
Multi-line asms
Multi-line assembler commands, including Fift code snippets, can be defined using triple-quoted strings"""
.
For instance:
Stack calling conventions
The syntax for arguments and returns is the same as for standard functions, but there is one caveat - argument values are pushed onto the stack before the function body is executed, and the return type is what is captured from the stack afterward.Arguments
When calling an asm function, the first argument is pushed onto the stack first, the second one second, and so on, so that the first argument is at the bottom of the stack and the last one at the top.Returns
An assembler function’s return type attempts to grab relevant values from the resulting stack after the function execution and any result rearrangements. Specifying an atomic type, such as anint
, cell
, or builder
, will make the assembler function
capture the top value from the stack.
For example, in the function,
STVARUINT16
produces a final builder at the top of the stack, which is returned by the storeCoins
function.
Specifying a tensor type as a return type, such as (int, int)
, will cause the assembler function to take as many elements from the stack as
the number of components in the tensor type. If the tensor type has nested tensor types, like ((int, int), int)
,
it is interpreted as if it was the flattened tensor type (int, int, int)
.
For example, this function duplicates its input, so that if the input is 5
, it returns the tensor (5, 5)
.
Stack registers
The so-called stack registers are a way of referring to the values at the top of the stack. In total, there are 256 stack registers, i.e., values held on the stack at any given time. Registers0
is the value at the top of the stack, register s1
is the value immediately after it, and so on,
until we reach the bottom of the stack, represented by s255
, i.e., the 256th stack register.
When a value x
is pushed onto the stack, it becomes the new s0
. At the same time, the old s0
becomes the new s1
, the old s1
becomes the new s2
, and so on.
Rearranging stack entries
When manually rearranging arguments, they are evaluated in the new order.
To overwrite this behavior see
#pragma compute-asm-ltr
.- The function takes arguments in the order specified by the parameters.
- If an argument arrangement is present, arguments are reordered before being pushed onto the stack.
- The function body is executed.
- If a result arrangement is present, resulting values are reordered on the stack.
- The resulting values are captured (partially or fully) by the return type of the function.
asm(arg2 arg1)
, where arg1
and arg2
are some arguments of the function arranged in the order we want to push them
onto the stack: arg1
will be pushed first and placed at the bottom of the stack, while arg2
will be pushed last and placed at the top of the stack.
Arrangements are not limited to two arguments and operate on all parameters of the function.
asm(-> 1 0)
, where 1 and 0 represent a
left-to-right reordering of stack registers s1
and s0
, respectively.
The contents of s1
will be at the top of the stack, followed by the contents of s0
.
Arrangements are not limited to two return values and operate on captured values.
asm(arg2 arg1 -> 1 0)
.