(func $SAR
    (local $sp i32)
    (local $x1 i64)
    (local $x2 i64)
    (local $x3 i64)
    (local $x4 i64)
    (local $y1 i64)
    (local $y2 i64)
    (local $y3 i64)
    (local $y4 i64)

    (local $z1 i64)
    (local $z2 i64)
    (local $z3 i64)
    (local $z4 i64)

    ;; load args from the stack
    (local.set $x1 (i64.load (i32.add (global.get $sp) (i32.const 24))))
    (local.set $x2 (i64.load (i32.add (global.get $sp) (i32.const 16))))
    (local.set $x3 (i64.load (i32.add (global.get $sp) (i32.const 8))))
    (local.set $x4 (i64.load (global.get $sp)))

    (local.set $sp (i32.sub (global.get $sp) (i32.const 32)))

    (local.set $y1 (i64.load (i32.add (local.get $sp) (i32.const 24))))
    (local.set $y2 (i64.load (i32.add (local.get $sp) (i32.const 16))))
    (local.set $y3 (i64.load (i32.add (local.get $sp) (i32.const 8))))
    (local.set $y4 (i64.load (local.get $sp)))

    (block $label_sar_internal
        (if (i64.gt_u (i64.clz (local.get $y1)) (i64.const 0)) (then
            (block
                (local.set $z1 (call $shr_ (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4) (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)))
                (local.set $z2 (global.get $global_))
                (local.set $z3 (global.get $global__1))
                (local.set $z4 (global.get $global__2))

            )
            (br $label_sar_internal)
        ))
        (if (call $gte_256x256_64 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4) (i64.const 0) (i64.const 0) (i64.const 0) (i64.const 256)) (then
            (local.set $z1 (i64.const 18446744073709551615))
            (local.set $z2 (i64.const 18446744073709551615))
            (local.set $z3 (i64.const 18446744073709551615))
            (local.set $z4 (i64.const 18446744073709551615))
        ))
        (if (call $lt_256x256_64 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4) (i64.const 0) (i64.const 0) (i64.const 0) (i64.const 256)) (then
            (block
                (local.set $y1 (call $shr_ (i64.const 0) (i64.const 0) (i64.const 0) (local.get $x4) (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)))
                (local.set $y2 (global.get $global_))
                (local.set $y3 (global.get $global__1))
                (local.set $y4 (global.get $global__2))

            )
            (block
                (local.set $z1 (call $shl_ (i64.const 0) (i64.const 0) (i64.const 0) (i64.sub (i64.const 256) (local.get $x4)) (i64.const 18446744073709551615) (i64.const 18446744073709551615) (i64.const 18446744073709551615) (i64.const 18446744073709551615)))
                (local.set $z2 (global.get $global_))
                (local.set $z3 (global.get $global__1))
                (local.set $z4 (global.get $global__2))

            )
            (block
                (local.set $z1 (call $or_ (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4) (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4)))
                (local.set $z2 (global.get $global_))
                (local.set $z3 (global.get $global__1))
                (local.set $z4 (global.get $global__2))

            )
        ))

    )
    (i64.store (i32.add (local.get $sp) (i32.const 24)) (local.get $z1))
    (i64.store (i32.add (local.get $sp) (i32.const 16)) (local.get $z2))
    (i64.store (i32.add (local.get $sp) (i32.const  8)) (local.get $z3))
    (i64.store          (local.get $sp)                 (local.get $z4))
)

(func $lt_256x256_64
    (param $x1 i64)
    (param $x2 i64)
    (param $x3 i64)
    (param $x4 i64)
    (param $y1 i64)
    (param $y2 i64)
    (param $y3 i64)
    (param $y4 i64)
    (result i32)
    (local $z i32)
    (local $condition_106 i32)
    (local $condition_107 i32)
    (local $condition_108 i32)
    (block
        (block
            (local.set $condition_106 (call $cmp (local.get $x1) (local.get $y1)))
            (if (i32.eq (local.get $condition_106) (i32.const 0)) (then
                (block
                    (local.set $condition_107 (call $cmp (local.get $x2) (local.get $y2)))
                    (if (i32.eq (local.get $condition_107) (i32.const 0)) (then
                        (block
                            (local.set $condition_108 (call $cmp (local.get $x3) (local.get $y3)))
                            (if (i32.eq (local.get $condition_108) (i32.const 0)) (then
                                (local.set $z (i64.lt_u (local.get $x4) (local.get $y4)))
                            )(else
                                (if (i32.eq (local.get $condition_108) (i32.const 1)) (then
                                    (local.set $z (i32.const 0))
                                )(else
                                    (local.set $z (i32.const 1))
                                ))
                            ))

                        )
                    )(else
                        (if (i32.eq (local.get $condition_107) (i32.const 1)) (then
                            (local.set $z (i32.const 0))
                        )(else
                            (local.set $z (i32.const 1))
                        ))
                    ))

                )
            )(else
                (if (i32.eq (local.get $condition_106) (i32.const 1)) (then
                    (local.set $z (i32.const 0))
                )(else
                    (local.set $z (i32.const 1))
                ))
            ))

        )

    )
    (local.get $z)
)

(func $gte_256x256_64
    (param $x1 i64)
    (param $x2 i64)
    (param $x3 i64)
    (param $x4 i64)
    (param $y1 i64)
    (param $y2 i64)
    (param $y3 i64)
    (param $y4 i64)
    (result i32)
    (local $z i32)
    (block
        (local.set $z (i32.eqz (call $lt_256x256_64 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4) (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))))

    )
    (local.get $z)
)


(func $or_
    (param $x1 i64)
    (param $x2 i64)
    (param $x3 i64)
    (param $x4 i64)
    (param $y1 i64)
    (param $y2 i64)
    (param $y3 i64)
    (param $y4 i64)
    (result i64)
    (local $r1 i64)
    (local $r2 i64)
    (local $r3 i64)
    (local $r4 i64)
    (block
        (local.set $r1 (i64.or (local.get $x1) (local.get $y1)))
        (local.set $r2 (i64.or (local.get $x2) (local.get $y2)))
        (local.set $r3 (i64.or (local.get $x3) (local.get $y3)))
        (local.set $r4 (i64.or (local.get $x4) (local.get $y4)))

    )
    (global.set $global_ (local.get $r2))
    (global.set $global__1 (local.get $r3))
    (global.set $global__2 (local.get $r4))
    (local.get $r1)
)


(func $cmp
    (param $a i64)
    (param $b i64)
    (result i32)
    (local $r i32)
    (block
        (local.set $r (select (i32.const 4294967295) (i64.ne (local.get $a) (local.get $b)) (i64.lt_u (local.get $a) (local.get $b))))

    )
    (local.get $r)
)


(func $shr_single_
    (param $a i64)
    (param $amount i64)
    (result i64)
    (local $x i64)
    (local $y i64)
    (block
        (local.set $y (i64.shl (local.get $a) (i64.sub (i64.const 64) (local.get $amount))))
        (local.set $x (i64.shr_u (local.get $a) (local.get $amount)))

    )
    (global.set $global_ (local.get $y))
    (local.get $x)
)

(func $shr_
    (param $x1 i64)
    (param $x2 i64)
    (param $x3 i64)
    (param $x4 i64)
    (param $y1 i64)
    (param $y2 i64)
    (param $y3 i64)
    (param $y4 i64)
    (result i64)
    (local $z1 i64)
    (local $z2 i64)
    (local $z3 i64)
    (local $z4 i64)
    (local $t i64)
    (block
        (if (i32.and (i64.eqz (local.get $x1)) (i64.eqz (local.get $x2))) (then
            (if (i64.eqz (local.get $x3)) (then
                (if (i64.eqz (local.get $x4))
                    (then
                        (local.set $z1 (local.get $y1))
                        (local.set $z2 (local.get $y2))
                        (local.set $z3 (local.get $y3))
                        (local.set $z4 (local.get $y4))
                    )
                    (else
                        (if (i64.lt_u (local.get $x4) (i64.const 256)) (then
                            (if (i64.ge_u (local.get $x4) (i64.const 128)) (then
                                (local.set $y4 (local.get $y2))
                                (local.set $y3 (local.get $y1))
                                (local.set $y2 (i64.const 0))
                                (local.set $y1 (i64.const 0))
                                (local.set $x4 (i64.sub (local.get $x4) (i64.const 128)))
                            ))
                            (if (i64.ge_u (local.get $x4) (i64.const 64)) (then
                                (local.set $y4 (local.get $y3))
                                (local.set $y3 (local.get $y2))
                                (local.set $y2 (local.get $y1))
                                (local.set $y1 (i64.const 0))
                                (local.set $x4 (i64.sub (local.get $x4) (i64.const 64)))
                            ))
                            (nop)
                            (block
                                (local.set $z4 (call $shr_single_ (local.get $y4) (local.get $x4)))
                                (local.set $t (global.get $global_))

                            )
                            (block
                                (local.set $z3 (call $shr_single_ (local.get $y3) (local.get $x4)))
                                (local.set $t (global.get $global_))

                            )
                            (local.set $z4 (i64.or (local.get $z4) (local.get $t)))
                            (block
                                (local.set $z2 (call $shr_single_ (local.get $y2) (local.get $x4)))
                                (local.set $t (global.get $global_))

                            )
                            (local.set $z3 (i64.or (local.get $z3) (local.get $t)))
                            (block
                                (local.set $z1 (call $shr_single_ (local.get $y1) (local.get $x4)))
                                (local.set $t (global.get $global_))

                            )
                            (local.set $z2 (i64.or (local.get $z2) (local.get $t)))
                        ))
                    )
                )
            ))
        ))

    )
    (global.set $global_ (local.get $z2))
    (global.set $global__1 (local.get $z3))
    (global.set $global__2 (local.get $z4))
    (local.get $z1)
)


(func $shl_single_
    (param $a i64)
    (param $amount i64)
    (result i64)
    (local $x i64)
    (local $y i64)
    (block
        (local.set $x (i64.shr_u (local.get $a) (i64.sub (i64.const 64) (local.get $amount))))
        (local.set $y (i64.shl (local.get $a) (local.get $amount)))

    )
    (global.set $global_ (local.get $y))
    (local.get $x)
)

(func $shl_
    (param $x1 i64)
    (param $x2 i64)
    (param $x3 i64)
    (param $x4 i64)
    (param $y1 i64)
    (param $y2 i64)
    (param $y3 i64)
    (param $y4 i64)
    (result i64)
    (local $z1 i64)
    (local $z2 i64)
    (local $z3 i64)
    (local $z4 i64)
    (local $t i64)
    (local $r i64)
    (block
        (if (i32.and (i64.eqz (local.get $x1)) (i64.eqz (local.get $x2))) (then
            (if (i64.eqz (local.get $x3)) (then
                (if (i64.eqz (local.get $x4))
                    (then
                        (local.set $z1 (local.get $y1))
                        (local.set $z2 (local.get $y2))
                        (local.set $z3 (local.get $y3))
                        (local.set $z4 (local.get $y4))
                    )
                    (else
                        (if (i64.lt_u (local.get $x4) (i64.const 256)) (then
                            (if (i64.ge_u (local.get $x4) (i64.const 128)) (then
                                (local.set $y1 (local.get $y3))
                                (local.set $y2 (local.get $y4))
                                (local.set $y3 (i64.const 0))
                                (local.set $y4 (i64.const 0))
                                (local.set $x4 (i64.sub (local.get $x4) (i64.const 128)))
                            ))
                            (if (i64.ge_u (local.get $x4) (i64.const 64)) (then
                                (local.set $y1 (local.get $y2))
                                (local.set $y2 (local.get $y3))
                                (local.set $y3 (local.get $y4))
                                (local.set $y4 (i64.const 0))
                                (local.set $x4 (i64.sub (local.get $x4) (i64.const 64)))
                            ))
                            (nop)
                            (block
                                (local.set $t (call $shl_single_ (local.get $y4) (local.get $x4)))
                                (local.set $z4 (global.get $global_))

                            )
                            (block
                                (local.set $r (call $shl_single_ (local.get $y3) (local.get $x4)))
                                (local.set $z3 (global.get $global_))

                            )
                            (local.set $z3 (i64.or (local.get $z3) (local.get $t)))
                            (block
                                (local.set $t (call $shl_single_ (local.get $y2) (local.get $x4)))
                                (local.set $z2 (global.get $global_))

                            )
                            (local.set $z2 (i64.or (local.get $z2) (local.get $r)))
                            (block
                                (local.set $r (call $shl_single_ (local.get $y1) (local.get $x4)))
                                (local.set $z1 (global.get $global_))

                            )
                            (local.set $z1 (i64.or (local.get $z1) (local.get $t)))
                        ))
                    )
                )
            ))
        ))

    )
    (global.set $global_ (local.get $z2))
    (global.set $global__1 (local.get $z3))
    (global.set $global__2 (local.get $z4))
    (local.get $z1)
)
