(module
  (memory 1)

  (type $i32_T (func (param i32 i32) (result i32)))
  (type $i64_T (func (param i64 i64) (result i32)))
  (type $f32_T (func (param f32 f32) (result i32)))
  (type $f64_T (func (param f64 f64) (result i32)))
  (table funcref
    (elem $i32_t0 $i32_t1 $i64_t0 $i64_t1 $f32_t0 $f32_t1 $f64_t0 $f64_t1)
  )

  (func $i32_t0 (type $i32_T) (i32.const -1))
  (func $i32_t1 (type $i32_T) (i32.const -2))
  (func $i64_t0 (type $i64_T) (i32.const -1))
  (func $i64_t1 (type $i64_T) (i32.const -2))
  (func $f32_t0 (type $f32_T) (i32.const -1))
  (func $f32_t1 (type $f32_T) (i32.const -2))
  (func $f64_t0 (type $f64_T) (i32.const -1))
  (func $f64_t1 (type $f64_T) (i32.const -2))

  ;; The idea is: We reset the memory, then the instruction call $*_left,
  ;; $*_right, $*_another, $*_callee (for indirect calls), and $*_bool (when a
  ;; boolean value is needed). These functions all call bump, which shifts the
  ;; memory starting at address 8 up a byte, and then store a unique value at
  ;; address 8. Then we read the 4-byte value at address 8. It should contain
  ;; the correct sequence of unique values if the calls were evaluated in the
  ;; correct order.

  (func $reset (i32.store (i32.const 8) (i32.const 0)))

  (func $bump
    (i32.store8 (i32.const 11) (i32.load8_u (i32.const 10)))
    (i32.store8 (i32.const 10) (i32.load8_u (i32.const 9)))
    (i32.store8 (i32.const 9) (i32.load8_u (i32.const 8)))
    (i32.store8 (i32.const 8) (i32.const -3)))

  (func $get (result i32) (i32.load (i32.const 8)))

  (func $i32_left (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 1)) (i32.const 0))
  (func $i32_right (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 2)) (i32.const 1))
  (func $i32_another (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 3)) (i32.const 1))
  (func $i32_callee (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 4)) (i32.const 0))
  (func $i32_bool (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 5)) (i32.const 0))
  (func $i64_left (result i64) (call $bump) (i32.store8 (i32.const 8) (i32.const 1)) (i64.const 0))
  (func $i64_right (result i64) (call $bump) (i32.store8 (i32.const 8) (i32.const 2)) (i64.const 1))
  (func $i64_another (result i64) (call $bump) (i32.store8 (i32.const 8) (i32.const 3)) (i64.const 1))
  (func $i64_callee (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 4)) (i32.const 2))
  (func $i64_bool (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 5)) (i32.const 0))
  (func $f32_left (result f32) (call $bump) (i32.store8 (i32.const 8) (i32.const 1)) (f32.const 0))
  (func $f32_right (result f32) (call $bump) (i32.store8 (i32.const 8) (i32.const 2)) (f32.const 1))
  (func $f32_another (result f32) (call $bump) (i32.store8 (i32.const 8) (i32.const 3)) (f32.const 1))
  (func $f32_callee (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 4)) (i32.const 4))
  (func $f32_bool (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 5)) (i32.const 0))
  (func $f64_left (result f64) (call $bump) (i32.store8 (i32.const 8) (i32.const 1)) (f64.const 0))
  (func $f64_right (result f64) (call $bump) (i32.store8 (i32.const 8) (i32.const 2)) (f64.const 1))
  (func $f64_another (result f64) (call $bump) (i32.store8 (i32.const 8) (i32.const 3)) (f64.const 1))
  (func $f64_callee (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 4)) (i32.const 6))
  (func $f64_bool (result i32) (call $bump) (i32.store8 (i32.const 8) (i32.const 5)) (i32.const 0))
  (func $i32_dummy (param i32 i32))
  (func $i64_dummy (param i64 i64))
  (func $f32_dummy (param f32 f32))
  (func $f64_dummy (param f64 f64))

  (func (export "i32_add") (result i32) (call $reset) (drop (i32.add (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_sub") (result i32) (call $reset) (drop (i32.sub (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_mul") (result i32) (call $reset) (drop (i32.mul (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_div_s") (result i32) (call $reset) (drop (i32.div_s (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_div_u") (result i32) (call $reset) (drop (i32.div_u (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_rem_s") (result i32) (call $reset) (drop (i32.rem_s (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_rem_u") (result i32) (call $reset) (drop (i32.rem_u (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_and") (result i32) (call $reset) (drop (i32.and (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_or") (result i32) (call $reset) (drop (i32.or (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_xor") (result i32) (call $reset) (drop (i32.xor (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_shl") (result i32) (call $reset) (drop (i32.shl (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_shr_u") (result i32) (call $reset) (drop (i32.shr_u (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_shr_s") (result i32) (call $reset) (drop (i32.shr_s (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_eq") (result i32) (call $reset) (drop (i32.eq (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_ne") (result i32) (call $reset) (drop (i32.ne (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_lt_s") (result i32) (call $reset) (drop (i32.lt_s (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_le_s") (result i32) (call $reset) (drop (i32.le_s (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_lt_u") (result i32) (call $reset) (drop (i32.lt_u (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_le_u") (result i32) (call $reset) (drop (i32.le_u (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_gt_s") (result i32) (call $reset) (drop (i32.gt_s (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_ge_s") (result i32) (call $reset) (drop (i32.ge_s (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_gt_u") (result i32) (call $reset) (drop (i32.gt_u (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_ge_u") (result i32) (call $reset) (drop (i32.ge_u (call $i32_left) (call $i32_right))) (call $get))
  (func (export "i32_store") (result i32) (call $reset) (i32.store (call $i32_left) (call $i32_right)) (call $get))
  (func (export "i32_store8") (result i32) (call $reset) (i32.store8 (call $i32_left) (call $i32_right)) (call $get))
  (func (export "i32_store16") (result i32) (call $reset) (i32.store16 (call $i32_left) (call $i32_right)) (call $get))
  (func (export "i32_call") (result i32) (call $reset) (call $i32_dummy (call $i32_left) (call $i32_right)) (call $get))
  (func (export "i32_call_indirect") (result i32) (call $reset) (drop (call_indirect (type $i32_T) (call $i32_left) (call $i32_right) (call $i32_callee))) (call $get))
  (func (export "i32_select") (result i32) (call $reset) (drop (select (call $i32_left) (call $i32_right) (call $i32_bool))) (call $get))

  (func (export "i64_add") (result i32) (call $reset) (drop (i64.add (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_sub") (result i32) (call $reset) (drop (i64.sub (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_mul") (result i32) (call $reset) (drop (i64.mul (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_div_s") (result i32) (call $reset) (drop (i64.div_s (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_div_u") (result i32) (call $reset) (drop (i64.div_u (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_rem_s") (result i32) (call $reset) (drop (i64.rem_s (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_rem_u") (result i32) (call $reset) (drop (i64.rem_u (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_and") (result i32) (call $reset) (drop (i64.and (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_or") (result i32) (call $reset) (drop (i64.or (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_xor") (result i32) (call $reset) (drop (i64.xor (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_shl") (result i32) (call $reset) (drop (i64.shl (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_shr_u") (result i32) (call $reset) (drop (i64.shr_u (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_shr_s") (result i32) (call $reset) (drop (i64.shr_s (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_eq") (result i32) (call $reset) (drop (i64.eq (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_ne") (result i32) (call $reset) (drop (i64.ne (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_lt_s") (result i32) (call $reset) (drop (i64.lt_s (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_le_s") (result i32) (call $reset) (drop (i64.le_s (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_lt_u") (result i32) (call $reset) (drop (i64.lt_u (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_le_u") (result i32) (call $reset) (drop (i64.le_u (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_gt_s") (result i32) (call $reset) (drop (i64.gt_s (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_ge_s") (result i32) (call $reset) (drop (i64.ge_s (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_gt_u") (result i32) (call $reset) (drop (i64.gt_u (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_ge_u") (result i32) (call $reset) (drop (i64.ge_u (call $i64_left) (call $i64_right))) (call $get))
  (func (export "i64_store") (result i32) (call $reset) (i64.store (call $i32_left) (call $i64_right)) (call $get))
  (func (export "i64_store8") (result i32) (call $reset) (i64.store8 (call $i32_left) (call $i64_right)) (call $get))
  (func (export "i64_store16") (result i32) (call $reset) (i64.store16 (call $i32_left) (call $i64_right)) (call $get))
  (func (export "i64_store32") (result i32) (call $reset) (i64.store32 (call $i32_left) (call $i64_right)) (call $get))
  (func (export "i64_call") (result i32) (call $reset) (call $i64_dummy (call $i64_left) (call $i64_right)) (call $get))
  (func (export "i64_call_indirect") (result i32) (call $reset) (drop (call_indirect (type $i64_T) (call $i64_left) (call $i64_right) (call $i64_callee))) (call $get))
  (func (export "i64_select") (result i32) (call $reset) (drop (select (call $i64_left) (call $i64_right) (call $i64_bool))) (call $get))

  (func (export "f32_add") (result i32) (call $reset) (drop (f32.add (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_sub") (result i32) (call $reset) (drop (f32.sub (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_mul") (result i32) (call $reset) (drop (f32.mul (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_div") (result i32) (call $reset) (drop (f32.div (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_copysign") (result i32) (call $reset) (drop (f32.copysign (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_eq") (result i32) (call $reset) (drop (f32.eq (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_ne") (result i32) (call $reset) (drop (f32.ne (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_lt") (result i32) (call $reset) (drop (f32.lt (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_le") (result i32) (call $reset) (drop (f32.le (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_gt") (result i32) (call $reset) (drop (f32.gt (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_ge") (result i32) (call $reset) (drop (f32.ge (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_min") (result i32) (call $reset) (drop (f32.min (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_max") (result i32) (call $reset) (drop (f32.max (call $f32_left) (call $f32_right))) (call $get))
  (func (export "f32_store") (result i32) (call $reset) (f32.store (call $i32_left) (call $f32_right)) (call $get))
  (func (export "f32_call") (result i32) (call $reset) (call $f32_dummy (call $f32_left) (call $f32_right)) (call $get))
  (func (export "f32_call_indirect") (result i32) (call $reset) (drop (call_indirect (type $f32_T) (call $f32_left) (call $f32_right) (call $f32_callee))) (call $get))
  (func (export "f32_select") (result i32) (call $reset) (drop (select (call $f32_left) (call $f32_right) (call $f32_bool))) (call $get))

  (func (export "f64_add") (result i32) (call $reset) (drop (f64.add (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_sub") (result i32) (call $reset) (drop (f64.sub (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_mul") (result i32) (call $reset) (drop (f64.mul (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_div") (result i32) (call $reset) (drop (f64.div (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_copysign") (result i32) (call $reset) (drop (f64.copysign (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_eq") (result i32) (call $reset) (drop (f64.eq (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_ne") (result i32) (call $reset) (drop (f64.ne (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_lt") (result i32) (call $reset) (drop (f64.lt (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_le") (result i32) (call $reset) (drop (f64.le (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_gt") (result i32) (call $reset) (drop (f64.gt (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_ge") (result i32) (call $reset) (drop (f64.ge (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_min") (result i32) (call $reset) (drop (f64.min (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_max") (result i32) (call $reset) (drop (f64.max (call $f64_left) (call $f64_right))) (call $get))
  (func (export "f64_store") (result i32) (call $reset) (f64.store (call $i32_left) (call $f64_right)) (call $get))
  (func (export "f64_call") (result i32) (call $reset) (call $f64_dummy (call $f64_left) (call $f64_right)) (call $get))
  (func (export "f64_call_indirect") (result i32) (call $reset) (drop (call_indirect (type $f64_T) (call $f64_left) (call $f64_right) (call $f64_callee))) (call $get))
  (func (export "f64_select") (result i32) (call $reset) (drop (select (call $f64_left) (call $f64_right) (call $f64_bool))) (call $get))

  (func (export "br_if") (result i32)
    (block (result i32)
      (call $reset)
      (drop (br_if 0 (call $i32_left) (i32.and (call $i32_right) (i32.const 0))))
      (call $get)
    )
  )
  (func (export "br_table") (result i32)
    (block $a (result i32)
      (call $reset)
      (drop
        (block $b (result i32)
          (br_table $a $b (call $i32_left) (call $i32_right))
        )
      )
      (call $get)
    )
  )
)

(assert_return (invoke "i32_add") (i32.const 0x0102))     (assert_return (invoke "i64_add") (i32.const 0x0102))
(assert_return (invoke "i32_sub") (i32.const 0x0102))     (assert_return (invoke "i64_sub") (i32.const 0x0102))
(assert_return (invoke "i32_mul") (i32.const 0x0102))     (assert_return (invoke "i64_mul") (i32.const 0x0102))
(assert_return (invoke "i32_div_s") (i32.const 0x0102))   (assert_return (invoke "i64_div_s") (i32.const 0x0102))
(assert_return (invoke "i32_div_u") (i32.const 0x0102))   (assert_return (invoke "i64_div_u") (i32.const 0x0102))
(assert_return (invoke "i32_rem_s") (i32.const 0x0102))   (assert_return (invoke "i64_rem_s") (i32.const 0x0102))
(assert_return (invoke "i32_rem_u") (i32.const 0x0102))   (assert_return (invoke "i64_rem_u") (i32.const 0x0102))
(assert_return (invoke "i32_and") (i32.const 0x0102))     (assert_return (invoke "i64_and") (i32.const 0x0102))
(assert_return (invoke "i32_or") (i32.const 0x0102))      (assert_return (invoke "i64_or") (i32.const 0x0102))
(assert_return (invoke "i32_xor") (i32.const 0x0102))     (assert_return (invoke "i64_xor") (i32.const 0x0102))
(assert_return (invoke "i32_shl") (i32.const 0x0102))     (assert_return (invoke "i64_shl") (i32.const 0x0102))
(assert_return (invoke "i32_shr_u") (i32.const 0x0102))   (assert_return (invoke "i64_shr_u") (i32.const 0x0102))
(assert_return (invoke "i32_shr_s") (i32.const 0x0102))   (assert_return (invoke "i64_shr_s") (i32.const 0x0102))
(assert_return (invoke "i32_eq") (i32.const 0x0102))      (assert_return (invoke "i64_eq") (i32.const 0x0102))
(assert_return (invoke "i32_ne") (i32.const 0x0102))      (assert_return (invoke "i64_ne") (i32.const 0x0102))
(assert_return (invoke "i32_lt_s") (i32.const 0x0102))    (assert_return (invoke "i64_lt_s") (i32.const 0x0102))
(assert_return (invoke "i32_le_s") (i32.const 0x0102))    (assert_return (invoke "i64_le_s") (i32.const 0x0102))
(assert_return (invoke "i32_lt_u") (i32.const 0x0102))    (assert_return (invoke "i64_lt_u") (i32.const 0x0102))
(assert_return (invoke "i32_le_u") (i32.const 0x0102))    (assert_return (invoke "i64_le_u") (i32.const 0x0102))
(assert_return (invoke "i32_gt_s") (i32.const 0x0102))    (assert_return (invoke "i64_gt_s") (i32.const 0x0102))
(assert_return (invoke "i32_ge_s") (i32.const 0x0102))    (assert_return (invoke "i64_ge_s") (i32.const 0x0102))
(assert_return (invoke "i32_gt_u") (i32.const 0x0102))    (assert_return (invoke "i64_gt_u") (i32.const 0x0102))
(assert_return (invoke "i32_ge_u") (i32.const 0x0102))    (assert_return (invoke "i64_ge_u") (i32.const 0x0102))
(assert_return (invoke "i32_store") (i32.const 0x0102))   (assert_return (invoke "i64_store") (i32.const 0x0102))
(assert_return (invoke "i32_store8") (i32.const 0x0102))  (assert_return (invoke "i64_store8") (i32.const 0x0102))
(assert_return (invoke "i32_store16") (i32.const 0x0102)) (assert_return (invoke "i64_store16") (i32.const 0x0102))
(assert_return (invoke "i64_store32") (i32.const 0x0102))
(assert_return (invoke "i32_call") (i32.const 0x0102))    (assert_return (invoke "i64_call") (i32.const 0x0102))
(assert_return (invoke "i32_call_indirect") (i32.const 0x010204))
(assert_return (invoke "i64_call_indirect") (i32.const 0x010204))
(assert_return (invoke "i32_select") (i32.const 0x010205))  (assert_return (invoke "i64_select") (i32.const 0x010205))

(assert_return (invoke "f32_add") (i32.const 0x0102))     (assert_return (invoke "f64_add") (i32.const 0x0102))
(assert_return (invoke "f32_sub") (i32.const 0x0102))     (assert_return (invoke "f64_sub") (i32.const 0x0102))
(assert_return (invoke "f32_mul") (i32.const 0x0102))     (assert_return (invoke "f64_mul") (i32.const 0x0102))
(assert_return (invoke "f32_div") (i32.const 0x0102))     (assert_return (invoke "f64_div") (i32.const 0x0102))
(assert_return (invoke "f32_copysign") (i32.const 0x0102))(assert_return (invoke "f64_copysign") (i32.const 0x0102))
(assert_return (invoke "f32_eq") (i32.const 0x0102))      (assert_return (invoke "f64_eq") (i32.const 0x0102))
(assert_return (invoke "f32_ne") (i32.const 0x0102))      (assert_return (invoke "f64_ne") (i32.const 0x0102))
(assert_return (invoke "f32_lt") (i32.const 0x0102))      (assert_return (invoke "f64_lt") (i32.const 0x0102))
(assert_return (invoke "f32_le") (i32.const 0x0102))      (assert_return (invoke "f64_le") (i32.const 0x0102))
(assert_return (invoke "f32_gt") (i32.const 0x0102))      (assert_return (invoke "f64_gt") (i32.const 0x0102))
(assert_return (invoke "f32_ge") (i32.const 0x0102))      (assert_return (invoke "f64_ge") (i32.const 0x0102))
(assert_return (invoke "f32_min") (i32.const 0x0102))     (assert_return (invoke "f64_min") (i32.const 0x0102))
(assert_return (invoke "f32_max") (i32.const 0x0102))     (assert_return (invoke "f64_max") (i32.const 0x0102))
(assert_return (invoke "f32_store") (i32.const 0x0102))   (assert_return (invoke "f64_store") (i32.const 0x0102))
(assert_return (invoke "f32_call") (i32.const 0x0102))    (assert_return (invoke "f64_call") (i32.const 0x0102))
(assert_return (invoke "f32_call_indirect") (i32.const 0x010204))
(assert_return (invoke "f64_call_indirect") (i32.const 0x010204))
(assert_return (invoke "f32_select") (i32.const 0x010205))  (assert_return (invoke "f64_select") (i32.const 0x010205))

(assert_return (invoke "br_if") (i32.const 0x0102))
(assert_return (invoke "br_table") (i32.const 0x0102))
