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

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

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

所以,我想也许有一些我不理解的rotl操作,我试着直接把双shift和或转换成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)
)

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

使用移位和按位方式模拟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)
)

所以…你知道哪里出了问题吗?

(我已经验证了,是的,正确的起始值实际上是我期望它们在线性内存中的地方,无论是通过直接检查TypeScript/JavaScript端的内存缓冲区,还是通过使用一个真正基本的WASM调试函数:(func $read (param $a i32) (result i32) (i32.load (local.get $a))))

我明白了。这一切都归结为这个表达式:

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

被向后。事实证明,参数的顺序是相反的!改成这样:

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

使一切正常。

最新更新