假设我有8个32位寄存器:
A 0-31 E 0-31
B 0-31 F 0-31
C 0-31 G 0-31
D 0-31 H 0-31
我希望他们的位重新排列如下:
A' := A0 E0 A8 E8 A16 E16 A24 E24 B0 F0 B8 F8 B16 F16 B24 F24 C0 G0 ...etc. H24
B' := A1 E1 A9 E9 A17 E17 A25 E25 B1 F1 B9 F9 B17 F17 B25 F25 C1 G1 ...etc. H25
C' := A2 E2 A10 E10 A18 E18 A26 E26 B2 ... etc.
D' := ... etc.
E' := ... etc.
F' := ... etc.
G' := ... etc.
H' := ... etc.
在C或ARM程序集中计算此混洗的最有效方法是什么? (所以没有SSE的intel,没有64位寄存器,没有足够的寄存器来包含输入和输出。)http://programming.sirrida.de/calcperm.php的计算器非常好,但它不容易扩展到多个单词。我相信它可以比当时选择一位的天真方式更有效率。
答案 0 :(得分:2)
如果你制作组件A0 _ _ _ _ _ _ _ A8 _ _ _ _ _ _ _ A16
等(只是简单的掩盖)。
与其他寄存器类似,您可以轻松实现:
A0 E0 B0 F0 C0 G0 D0 H0 A8 E8 ..
你可以用两个bit_permute_step转换成正确的顺序,如calcperm所示:
x = bit_permute_step(x, 0x00cc00cc, 6); // Bit index swap 1,3
x = bit_permute_step(x, 0x0000f0f0, 12); // Bit index swap 2,4
其他寄存器的类似故事,只是稍微偏移了一点。
基本上一次移动4位,只需要8次修复。
答案 1 :(得分:0)
; 1) Copy the top most 8 bits of H into the lowest bits of the output registers:
lsr H ; H.31 -> carry
rol H' ; carry -> H'.0
lsr H ; H.30 -> carry
rol G' ; carry -> G'.0
lsr H
rol F'
...
lsr H ; H.24 -> carry
rol A' ; carry to A'.0
; 2) go on with top 8 bits of D
lsr D ; D.31 -> carry
rol H' ; H'.0 -> H'.1 and carry -> H'.0
lsr D
rol G'
...
lsr D
rol A'
继续,直到所有位都到位。最后一步是
lsr A ; A.0 -> carry
rol A' ; A'.0 -> A'.1 -> A'.2 ... and carry -> A'.0
答案 2 :(得分:0)
我提出的最快版本:
// merges 32 bit a (low) and b (hi) into single 64 bit
#define m(a, b) (((uint64_t) (a)) | (((uint64_t) (b)) << 32))
// gets bit at position opos and moves it to position npos
#define s(a, opos, npos) (((opos) >= (npos)) ? (((a) & ( ((uint64_t)1) << (opos))) >> ((opos) - (npos))) : (((a) & (((uint64_t)1) << (opos))) << ((npos) - (opos))))
// gets 8 different bits from 64 bit number and puts them together into 1 byte, starting from idx
#define b(a, idx) (s(a, 0, idx) | s(a, 32, (idx - 1)) | s(a, 8, (idx - 2)) | s(a, 40, (idx - 3)) | s(a, 16, (idx - 4)) | s(a, 48, (idx - 5)) | s(a, 24, (idx - 6)) | s(a, 56, (idx - 7)))
// takes 8 32 bit registers in in, outputs in out
void shuffle(const uint32_t* in, uint32_t* out) {
uint64_t t[4] = { m(in[0], in[4]), m(in[1], in[5]), m(in[2], in[6]), m(in[3], in[7]) };
for (int i = 0; i < 8; i++, t[0] >>= 1, t[1] >>= 1, t[2] >>= 1, t[3] >>= 1)
out[i] = b(t[0], 31) | b(t[1], 23) | b(t[2], 15) | b(t[3], 7);
}
与直接方法相比,唯一的“优化”是将两个32位寄存器合并为单个64位,因此我们可以减少循环中的移位数
答案 3 :(得分:0)
在带有SSE的x86上:punpcklbw
(_mm_unpacklo_epi8
可以交错源regs的字节。
使用向量移位然后pmovmskb获取每个字节的高位,给出结果,如
A0 E0 A8 E8 A16 E16 A24 E24
然后组合这些字节结果以获得8个dest寄存器。这样很糟糕,因为它需要为每个结果字节移位/ pmovmskb。有8 * 4个结果字节,因此需要很多代码。