启用-O2或更高版本时,为什么此代码会中断?

hjf*_*hjf 7 c clang microchip pic xc8

我试图将NSA SPECK的实现安装在8位PIC微控制器中。他们的编译器的免费版本(基于CLANG)将无法进行优化,因此我的内存不足。我尝试了启用-O2,-O3和-Os(针对大小进行优化)的“试用版”。使用-Os可以使我的代码适合2K程序存储空间。

这是代码:

#include <stdint.h>
#include <string.h>

#define ROR(x, r) ((x >> r) | (x << (32 - r)))
#define ROL(x, r) ((x << r) | (x >> (32 - r)))
#define R(x, y, k) (x = ROR(x, 8), x += y, x ^= k, y = ROL(y, 3), y ^= x)
#define ROUNDS 27

void encrypt_block(uint32_t ct[2],
        uint32_t const pt[2],
        uint32_t const K[4]) {
    uint32_t x = pt[0], y = pt[1];
    uint32_t a = K[0], b = K[1], c = K[2], d = K[3];

    R(y, x, a);
    for (int i = 0; i < ROUNDS - 3; i += 3) {
        R(b, a, i);
        R(y, x, a);
        R(c, a, i + 1);
        R(y, x, a);
        R(d, a, i + 2);
        R(y, x, a);
    }
    R(b, a, ROUNDS - 3);
    R(y, x, a);
    R(c, a, ROUNDS - 2);
    R(y, x, a);

    ct[0] = x;
    ct[1] = y;
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,当逐行调试它时,将其与实施指南中第32页“ 15 SPECK64 / 128测试向量”中的测试向量进行比较,结果与预期结果有所不同。

这是调用此函数的一种方法:

uint32_t out[2];
uint32_t in[] = { 0x7475432d, 0x3b726574 };
uint32_t key[] = { 0x3020100, 0xb0a0908, 0x13121110, 0x1b1a1918 };

encrypt_block(out, in, key);

assert(out[0] == 0x454e028b);
assert(out[1] == 0x8c6fa548);
Run Code Online (Sandbox Code Playgroud)

根据指南,“出”的期望值应为0x454e028b, 0x8c6fa548。我使用-O2得到的结果是0x8FA3FED7 0x53D8CEA8。使用-O1,我得到 0x454e028b, 0x8c6fa548,这是正确的结果。

逐步调试

入门指南包括所有中间密钥计划的其他值,因此我逐行浏览了代码,并将结果与​​指南进行了比较。

为“X”的预期结果是:03020100131d0309bbd80d530d334df3。我开始进行逐步调试,但是当达到第4个结果时0d334df3,将显示调试器窗口0d334df0。到了下一轮,预期7fa43565值是7FA43578,只得到与每次迭代更糟。

仅当启用-O2或更高版本时,才会发生这种情况。在没有优化的情况下或在使用-O1的情况下,代码可以按预期工作。

hjf*_*hjf 4

这是编译器中的一个错误。

我在制造商论坛中发布了这个问题。其他人确实重现了这个问题,这是在编译某些部分时发生的。其他部分不受影响。

作为解决方法,我将宏更改为实际函数,并将操作分成两行:

uint32_t ROL(uint32_t x, uint8_t r) {
    uint32_t intermedio;
    intermedio = x << r;
    intermedio |= x >> (32 - r);
    return intermedio;
}
Run Code Online (Sandbox Code Playgroud)

这给出了正确的结果。