aboutsummaryrefslogtreecommitdiff
;; The idea is - we have 4 blocks, one inside another. We enter subsequent
;; blocks (how many we enter, depends on $blockfun's first arg) and then we
;; start leaving them. We "normally" (e.g. without br) exit some blocks (number
;; depending on $blockfun's 2nd arg) and then we break out of the outermost one
;; with the use of br_if.
;; When making this "path" through blocks, we also construct a result value.
;; The result value is initially 0 and upon each "normal" exit from a block,
;; one bit is being set to 1 in the result. Which bit is set, depends on the
;; block being left. Block 1 sets bit 0, block 2 sets bit 1, etc.
;; So, the entire function actually computes an integer with bits a1-a2 through
;; a1 set, with a1 and a2 being arguments to the function.
;; To keep things simple, we limit this to 4 block and 4 bits, that can be set

(module
 (memory 0 1)
 (func $blockfun
       (param $blocks_to_enter i32)
       (param $ones_to_set i32)
       (result i32)

       i32.const 0 ;; initial result value

       get_local $blocks_to_enter
       i32.const 0
       i32.eq
       br_if 0 ;; if number of blocks to enter is 0 - just return

       block $b1 (param i32) (result i32)
         get_local $blocks_to_enter
         i32.const 1
         i32.sub
         set_local $blocks_to_enter

         get_local $blocks_to_enter
         if (param i32) (result i32)
           block $b2 (param i32) (result i32)
             get_local $blocks_to_enter
             i32.const 1
             i32.sub
             set_local $blocks_to_enter

             get_local $blocks_to_enter
             if (param i32) (result i32)
               block $b3 (param i32) (result i32)
                 get_local $blocks_to_enter
                 i32.const 1
                 i32.sub
                 set_local $blocks_to_enter

                 get_local $blocks_to_enter
                 if (param i32) (result i32)
                   block $b4 (param i32) (result i32)
                     get_local $ones_to_set
                     i32.const 0
                     i32.eq
                     br_if $b1

                     get_local $ones_to_set
                     i32.const 1
                     i32.sub
                     set_local $ones_to_set

		     i32.const 8 ;; block $b4 adds (1 << 3) to the result
		     i32.add
                   end
                 end

                 get_local $ones_to_set
                 i32.const 0
                 i32.eq
                 br_if $b1

                 get_local $ones_to_set
                 i32.const 1
                 i32.sub
                 set_local $ones_to_set

                 i32.const 4 ;; block $b3 adds (1 << 2) to the result
                 i32.add
               end
             end

             get_local $ones_to_set
             i32.const 0
             i32.eq
             br_if $b1

             get_local $ones_to_set
             i32.const 1
             i32.sub
             set_local $ones_to_set

             i32.const 2 ;; block $b2 adds (1 << 1) to the result
             i32.add
           end
         end

         get_local $ones_to_set
         i32.const 0
         i32.eq
         br_if $b1

         get_local $ones_to_set
         i32.const 1
         i32.sub
         set_local $ones_to_set

         i32.const 1 ;; block $b1 adds (1 << 0) to the result
         i32.add
       end)
 (func $main
       (i32.store offset=0x0 align=4 (i32.const 0x0)
		  (call $blockfun (i32.const 4) (i32.const 2)))
       (i32.store offset=0x0 align=4 (i32.const 0x4)
                  (call $blockfun (i32.const 3) (i32.const 2)))
       (i32.store offset=0x0 align=4 (i32.const 0x8)
                  (call $blockfun (i32.const 2) (i32.const 0)))
       (i32.store offset=0x0 align=4 (i32.const 0xC)
		  (call $blockfun (i32.const 4) (i32.const 4)))
  )
 (export "main" (func $main)))