GCC为数组元素的重复XOR生成冗余代码

GCC正在给我一个困难的时间为以下源代码生成最佳的汇编:

memset(X, 0, 16);
for (int i= 0; i < 16; ++i) {
    X[0] ^= table[i][Y[i]].asQWord;
}

X是一个uint64_t [2]数组,
Y是一个unsigned char [16]数组,
表是一个二维数组的联合qword_t:

union qword_t {
    uint8_t asBytes[8];
    uint64_t asQWord;
};

const union qword_t table[16][256] = /* ... */;

使用选项-m64 -Ofast -mno-sse它会展开循环,并且每个具有赋值的xor都会产生3条指令(因此,总共发出的指令数量为3 * 16 = 48):

movzx  r9d, byte ptr [Y + i]                   ; extracting byte
xor    rax, qword ptr [table + r9*8 + SHIFT]   ; xoring, SHIFT = i * 0x800
mov    qword ptr [X], rax                      ; storing result

现在,我的理解是,结果X值可以在所有16个XOR中的rax寄存器中累积,然后可以存储在[X]地址,这可以通过这两个指令实现,每个xor的赋值为:

movzx  r9d, byte ptr [Y + i]                   ; extracting byte
xor    rax, qword ptr [table + r9*8 + SHIFT]   ; xoring, SHIFT = i * 0x800

和单一存储:

mov    qword ptr [X], rax                      ; storing result

(在这种情况下,指令的总数为2 * 16 1 = 33)

为什么GCC生成这些冗余的mov指令?我可以做些什么来避免这种情况?

附: C99,GCC 5.3.0,Intel Core i5 Sandy Bridge

为了避免这种情况,您可以使用它:

uint64_t v = 0;
for (int i= 0; i < 16; ++i) {
    v ^= table[i][Y[i]].asQWord;
}
X[0] = v;
X[1] = 0;

您可以轻松地注意到生成的指令在您的情况下是次优的,但由于不同的原因gcc可能无法确定。 (在这种情况下,gcc无法确定该表将永远不会访问与X相同的内存区域,如ecatmur更详细地解释。)

http://stackoverflow.com/questions/36592515/gcc-generates-redundant-code-for-repeated-xor-of-an-array-element

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:GCC为数组元素的重复XOR生成冗余代码