Most algorithms repeat the same block of code over and over until some condition is met. These types of loops can be performed in WAT using a small number of instructions. They do look different compared to other programming languages but they are simple to understand and easy to use.
Repeating the same block of code is normally called looping, and therefore the WAT loop
keyword is used to denote one of these sections of code,
but it doesn't really work in the way you may think.
;; Create loop section loop $name_of_loop_section ;; Do something end ;; After loop section
To look at this example, you may think that the execution will enter the loop, do something, and then return to the start of the loop again. This is not what happens however. Once the execution gets to the end of the loop, it will just continue moving on after it. It will not automatically return to the top of the loop.
To move back to the start of the section you need to call either the br
or br_if
instruction.
The “br” part does not stand for “break” but for “branch”.
These commands move the execution to the start of a loop block, it branches back up to the top, so that the inner part of the loop can be performed again.
;; Create loop section loop $name_of_loop_section ;; Do something ;; We want to do it again br $name_of_loop_section end
The br
instruction needs the name of the loop section, so it knows where to branch back to, where the next execution will be located at.
In this example we enter the loop section, do something and then branch back to the top, to repeat the loop again (forever in this case).
The br_if
instruction can only be used if the stack contains an i32
integer value that is non-zero.
It will pop the value off the stack and if it is zone then it does nothing and moves on to the next instruction.
;; Create loop section loop $name_of_loop_section ;; Do something ;; Push i32 value 1 onto the stack i32.const 1 ;; Check if we need to loop again br_if $name_of_loop_section end
Here we enter the loop block, do something, and then push a condition value onto the stack.
Following this is the br_if
instruction, which pops the condition value off the stack, checks if it is non-zero,
and if it is then it moves the execution up to the top of the loop section, ready to do something again.
If the value is zero then it will move past the instruction and continue on with the next instruction.
You will sometimes need to have a loop inside another loop. When branching to the start of the loop section you will need to make sure you are moving to the right parent loop location. To show how this is done we need to look at an example.
;; Create loop section 1 loop $loop_1 ;; Create loop section 2 loop $loop_2 ;; Do something ;; Push i32 value 0 onto the stack i32.const 0 ;; Check if we need to loop again br_if $loop_2 ;; Push i32 value 1 onto the stack i32.const 1 ;; Check if we need to loop again br_if $loop_1 end end
In this example we have two loops, one inside the other.
We have named each one differently, with the outer one called $loop_1
and the inner one named $loop_2
.
You can name the loops anyway you want.
As you can see, the branch instructions are given the name of the loop section they should jump to.
In this example, the first br_if
instruction will not branch, but the second one will, and will move the execution to the top of $loop_1
.
The branch instructions can be used with numbers instead of labels.
For example, the inner most loop is number 0 $loop_2
and the loop one level up from this is number 1 $loop_1
.
These numbers increase by one for each parent loop section.
It can get too complicated using numbers instead of labels, so I recommend you always use labels for your loops and blocks.
The block
instruction is just like the loop
instruction but instead of branching the execution to the top of the section,
it moves it to the end.
;; Create block section block $name_of_block_section ;; Do something ;; We want to stop and move past section br $name_of_block_section ;; Do something else (will not get here) end
Here we create the block and move inside it, performing the first part within, but we then call the branch instruction, which sends the execution to the end of the block section. This will bypass everything inside the section that comes after the branch instruction.
;; Create block section block $name_of_block_section ;; Do something ;; Push i32 value 1 onto the stack i32.const 1 ;; Check if we need to branch to end of section br_if $name_of_block_section ;; If condition is not met then this will be done end
In this example we use the br_if
instruction to see if we need to branch to the end of the block section.
If the condition is not met then it will not branch and will just continue on to the next instruction.
Once the execution gets to the end of the block it continues on, and will not move to the top of the block.
What you need to remember is, for loops the branch instructions move the execution to the top of the section, and for blocks, the branching moves to the end of the section. For loops the branch instruction is similar to the “continue” keyword in other programming languages, and for blocks they are similar to the “break” keyword.
Blocks can be nested just like loops can. You can nest different loops and blocks together as well if you want. You need to make all loop and block labels unique within each function.
Let's take a look at an example that puts all these parts together. In the below code we are adding up all the odd numbers together, between 0 and 99, to get a total amount.
(local $index i32) (local $total i32) ;; Set starting value of $index i32.const 0 local.set $index ;; for (var $index = 0; $index < 100; $index++) loop $loop_index block $block_index ;; Check if $index is odd local.get $index i32.const 1 i32.and ;; If odd if ;; Add index to total local.get $total local.get $index i32.add local.set $total end ;; Check $index is over 100 local.get $index i32.const 100 i32.ge_s br_if $block_index ;; Increase $index local.get $index i32.const 0x01 i32.add local.set $index ;; Loop again br $loop_index end end
At the start we are declaring two local variables.
For looping will use the $index
integer, which will increase this from 0 to 100 for each cycle of the loop that it goes through.
The $total
integer will keep the total amount, the sum of all the odd numbers.
Before we start the loops we need to set the starting value of the $index
variable to the value 0.
We will want to branch to the start of the loop, but we will also want to break out of the block after we have looped 100 times.
To do this we have included both a loop
and a block
together.
The order you do this does not matter.
We are nesting the block inside the loop but we have written it so that it looks like it is on the same level.
Both have a similar looking name too, so we can quickly see if we are branching the loop or the block section.
Inside the sections there are the looping tasks.
The first thing we do is check if the $index
is odd or not.
If it is odd then its value is added to the total.
After this we want to check if the $index
value has reached the 100 upper limit.
This uses the br_if
instruction, comparing it to 100, and if the condition is met then it will branch to the end of the $block_index
section.
This is the end of the block section and the end of the looping process.
If the index value is under the limit then it continues on within the loop section.
The next part is to increase the $index
value by one.
The final instruction inside the loop section is to branch to the start of the $loop_index
section, which moves the execution point to the start of the loop again.
After the loop has been performed 100 times the $total
value will contain the result.
Within the loop or block section we may need to pop values off the stack that were pushed onto it beforehand. You may also need to push something onto the stack before exiting the section, so that it can be used afterwards. Let's take a look at what this could look like.
;; Push i32 value 10 onto the stack i32.const 10 ;; Create block section block $name_of_block_section ;; Push i32 value 20 onto the stack i32.const 20 ;; Add the 10 and 20 together i32.add ;; Set $total value local.set $total end
In this example we are trying to add the value pushed onto the stack before the block began, the value 10, to the value pushed onto the stack within the block, the value 20. It looks like it should work, but it will not compile, and you will end up with an error message.
;; Create block section block $name_of_block_section ;; Push i32 value 20 onto the stack i32.const 20 end ;; Push i32 value 10 onto the stack i32.const 10 ;; Add the 10 and 20 together i32.add ;; Set $total value local.set $total
This time we are pushing a value onto the stack inside the block section, exiting the block section, then pushing another value onto the stack, but this time outside the block section, and finally adding them together. Again, this will not compile, and you will get another error message.
If you want to bring in data from off the stack when you are inside the section, or if you want to send out values from within the section,
by pushing them onto the stack, then you need to treat the section like a small internal function,
with parameters and results.
This is similar to how the stack is used with the if
instruction.
;; Push i32 value 10 onto the stack i32.const 10 ;; Create block section block $name_of_block_section (param i32) ;; Push i32 value 20 onto the stack i32.const 20 ;; Add the 10 and 20 together i32.add ;; Set $total value local.set $total end
We have changed the block
line to include a param
statement.
This is saying that it expects there to be an i32
value on the stack before it enters the block section.
;; Create block section block $name_of_block_section (result i32) ;; Push i32 value 20 onto the stack i32.const 20 end ;; Push i32 value 10 onto the stack i32.const 10 ;; Add the 10 and 20 together i32.add ;; Set $total value local.set $total
The block
line has been changed with the result
statement.
This states that the block section will push an i32
value onto the stack before it exits.
You can declare any number of parameters of any data type. You can also declare, at the same time if needed, any number of results with any data types required.