Flow control
As mentioned above, Miden assembly provides high-level constructs to facilitate flow control. These constructs are:
- if-else expressions for conditional execution.
- repeat expressions for bounded counter-controlled loops.
- while expressions for unbounded condition-controlled loops.
Conditional execution
Conditional execution in Miden VM can be accomplished with if-else statements. These statements can take one of the following forms:
if.true .. else .. end
This is the full form, when there is work to be done on both branches:
if.true
..instructions..
else
..instructions..
end
if.true .. end
This is the abbreviated form, for when there is only work to be done on one branch. In these cases the "unused" branch can be elided:
if.true
..instructions..
end
In addition to if.true
, there is also if.false
, which is identical in syntax, but for false-conditioned branches. It is equivalent in semantics to using if.true
and swapping the branches.
The body of each branch, i.e. ..instructions..
in the examples above, can be a sequence of zero or more instructions (an empty body is only valid so long as at least one branch is non-empty). These can consist of any instruction, including nested control flow.
tip
As with other control structures described below that have nested blocks,
it is essential that you ensure that the state of the operand stack is
consistent at join points in control flow. For example, with if.true
control flow implicitly joins at the end of each branch. If you have moved
items around on the operand stack, or added/removed items, and those
modifications would persist past the end of that branch, it is highly
recommended that you make equivalent modifications in the opposite branch.
This is not required if modifications are local to a block.
The semantics of the if.true
and if.false
control operator are as follows:
- The condition is popped from the top of the stack. It must be a boolean value, i.e. for false, for true. If the condition is not a boolean value, then execution traps.
- The conditional branch is chosen:
a. If the operator is
if.true
, and the condition is true, instructions in the first branch are executed; otherwise, if the condition is false, then the second branch is executed. If a branch was elided or empty, the assembler provides a default body consisting of a singlenop
instruction. b. If the operator isif.false
, the behavior is identical to that ofif.true
, except the condition must be false for the first branch to be taken, and true for the second branch. - Control joins at the next instruction immediately following the
if.true
/if.false
instruction.
tip
A note on performance: using if-else statements incurs a small, but non-negligible overhead. Thus, for simple conditional statements, it may be more efficient to compute the result of both branches, and then select the result using conditional drop instructions.
This does not apply to if-else statements whose bodies contain side-effects that cannot be easily adapted to this type of rewrite. For example, writing a value to global memory is a side effect, but if both branches would write to the same address, and only the value being written differs, then this can likely be rewritten to use cdrop
.
Counter-controlled loops
Executing a sequence of instructions a predefined number of times can be accomplished with repeat statements. These statements look like so:
repeat.<count>
<instructions>
end
where:
instructions
can be a sequence of any instructions, including nested control structures.count
is the number of times theinstructions
sequence should be repeated (e.g.repeat.10
).count
must be an integer or a constant greater than .
Note: During compilation the
repeat.<count>
blocks are unrolled and expanded into<count>
copies of its inner block, there is no additional cost for counting variables in this case.
Condition-controlled loops
Executing a sequence of instructions zero or more times based on some condition can be accomplished with while loop expressions. These expressions look like so:
while.true
<instructions>
end
where instructions
can be a sequence of any instructions, including nested control structures. The above does the following:
- Pops the top item from the stack.
- If the value of the item is ,
instructions
in the loop body are executed.- After the body is executed, the stack is popped again, and if the popped value is , the body is executed again.
- If the popped value is , the loop is exited.
- If the popped value is not binary, the execution fails.
- If the value of the item is , execution of loop body is skipped.
- If the value is not binary, the execution fails.
Example:
# push the boolean true to the stack
push.1
# pop the top element of the stack and loop while it is true
while.true
# push the boolean false to the stack, finishing the loop for the next iteration
push.0
end
No-op
While rare, there may be situations where you have an empty block and require a do-nothing placeholder instruction, or where you specifically want to advance the cycle counter without any side-effects. The nop
instruction can be used in these instances.
if.true
nop
else
..instructions..
end
In the example above, we do not want to perform any work if the condition is true, so we place a nop
in that branch. This explicit representation of "empty" blocks is automatically done by the assembler when parsing if.true
or if.false
in abbreviated form, or when one of the branches is empty.
The semantics of this instruction are to increment the cycle count, and that is it - no other effects.