我在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))
使一切正常。