[英]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.