繁体   English   中英

WebAssembly 中的位旋转

[英]Bit rotations in WebAssembly

我在 TypeScript 中有一段热代码,它做了一堆算术——特别是几个 32 位旋转。 例如,

  let a = t << 16 | t >>> 16; // rotate left by 16 bits
  let b = t << 12 | t >>> 20; // rotate left by 12 bits
  let c = t << 8 | t >>> 24; // rotate left by 8 bits
  let d = t << 7 | t >>> 25; // rotate left by 7 bits

我正在尝试将包含这些操作的 function 移植到 WebAssembly 以获得更好的性能,因为很多数学正是 WebAssembly 应该擅长的。 不幸的是,WebAssembly 版本最终给出了不正确的结果,我已经设法将错误缩小到精确的这些位旋转操作。

现在,WebAssembly 有一个方便的i32.rotl操作码,它应该自己替换双移位和按位或全部,但是在例如t = 1634760805 (0x61707865) 的情况下,TypeScript 代码给出的结果为 2019917000 (0x7665) ) 当旋转 16 位时,对十六进制数字的简单检查表明是正确的。 但是,将这一操作转移到 WebAssembly 会得到 512 的结果。

所以,我想也许对rotl操作有一些我不理解的地方,我尝试直接将 double-shift-and-or 转换为 WebAssembly ......它仍然产生 512 的结果。

我使用rotl操作码的测试代码如下所示:

  (func $rot (param $a i32) (param $r i32) (result i32)
    (local $xa i32)

    ;; x[a] = x[a] rotl r;
    (local.set $xa (i32.rotl (local.get $r) (i32.load (local.get $a))))
    (i32.store (local.get $a) (local.get $xa))
    (local.get $xa)
  )

它只是从 memory(由 $a 指向)中读取一个 32 位值,将其旋转 $r,将结果存储回线性 memory 并返回它。

使用移位和按位或按位模拟rotl的版本如下所示:

  (func $rot (param $a i32) (param $r i32) (result i32)
    (local $xa i32)

    ;; x[a] = x[a] rotl r;
    (local.set $xa (i32.load (local.get $a)))
    (local.set $xa (i32.or
      (i32.shl   (local.get $r) (local.get $xa))
      (i32.shr_u (i32.sub (i32.const 32) (local.get $r)) (local.get $xa))))
    (i32.store (local.get $a) (local.get $xa))
    (local.get $xa)
  )

所以......关于出了什么问题的任何想法?

(我已经验证了,是的,正确的起始值实际上是我期望它们在线性 memory 中的位置,既可以通过直接检查 TypeScript/JavaScript 端的 memory 缓冲区,也可以使用真正基本的 WASM 调试(func $read (param $a i32) (result i32) (i32.load (local.get $a))) )

嗯,我想通了。 这一切都归结为这个表达:

(i32.rotl (local.get $r) (i32.load (local.get $a)))

倒退。 结果是 arguments go 以其他顺序:将其更改为:

(i32.rotl (i32.load (local.get $a)) (local.get $r))

使一切正常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM