简体   繁体   English

WebAssembly 中的位旋转

[英]Bit rotations in WebAssembly

I have a hot bit of code in TypeScript which does a bunch of arithmetic--notably, several 32-bit rotations.我在 TypeScript 中有一段热代码,它做了一堆算术——特别是几个 32 位旋转。 Eg,例如,

  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

I am trying to port the function containing these operations over to WebAssembly to get better performance, since Lots of Math is exactly what WebAssembly should be good at.我正在尝试将包含这些操作的 function 移植到 WebAssembly 以获得更好的性能,因为很多数学正是 WebAssembly 应该擅长的。 Unfortunately, the WebAssembly version ends up giving incorrect results, and I have managed to narrow the error down to precisely these bit rotation operations.不幸的是,WebAssembly 版本最终给出了不正确的结果,我已经设法将错误缩小到精确的这些位旋转操作。

Now, WebAssembly has a convenient i32.rotl opcode which should replace the double shifts and bitwise or all on its own, but in the case of, for example, t = 1634760805 (0x61707865), the TypeScript code gives a result of 2019910000 (0x78656170) when rotating by 16 bits, which simple inspection of the hex digits shows to be correct.现在,WebAssembly 有一个方便的i32.rotl操作码,它应该自己替换双移位和按位或全部,但是在例如t = 1634760805 (0x61707865) 的情况下,TypeScript 代码给出的结果为 2019917000 (0x7665) ) 当旋转 16 位时,对十六进制数字的简单检查表明是正确的。 Moving just that one operation over to WebAssembly, though, gives a result of 512.但是,将这一操作转移到 WebAssembly 会得到 512 的结果。

So, I thought maybe there's something I don't understand about the rotl operation, and I tried just directly translating the double-shift-and-or into WebAssembly... and it still produces a result of 512.所以,我想也许对rotl操作有一些我不理解的地方,我尝试直接将 double-shift-and-or 转换为 WebAssembly ......它仍然产生 512 的结果。

My test code that uses the rotl opcode looks like this:我使用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)
  )

It just reads a 32-bit value out of memory (pointed to by $a), rotates it by $r, stores the result back in linear memory and returns it.它只是从 memory(由 $a 指向)中读取一个 32 位值,将其旋转 $r,将结果存储回线性 memory 并返回它。

The version that simulates rotl with shifts and bitwise or looks like this:使用移位和按位或按位模拟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)
  )

So... any ideas on what's going wrong?所以......关于出了什么问题的任何想法?

(I have already verified that yes, the correct starting values are in fact where I expect them to be in linear memory, both by directly inspecting the memory buffer on the TypeScript/JavaScript side and by using a really basic WASM debugging function: (func $read (param $a i32) (result i32) (i32.load (local.get $a))) ) (我已经验证了,是的,正确的起始值实际上是我期望它们在线性 memory 中的位置,既可以通过直接检查 TypeScript/JavaScript 端的 memory 缓冲区,也可以使用真正基本的 WASM 调试(func $read (param $a i32) (result i32) (i32.load (local.get $a))) )

Well, I figured it out.嗯,我想通了。 It all comes down to this expression:这一切都归结为这个表达:

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

Being backwards.倒退。 Turns out the arguments go in the other order: Changing it to this:结果是 arguments go 以其他顺序:将其更改为:

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

makes everything work.使一切正常。

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

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