为什么启用 -O2 或更高版本时此代码会中断?
Why does this code break when -O2 or higher is enabled?
我试图在 8 位 PIC 微控制器中安装 NSA SPECK 的实现。
他们的编译器(基于 CLANG)的免费版本不会启用优化,所以我 运行 内存不足。我尝试了启用 -O2、-O3 和 -Os(针对大小进行优化)的 "trial" 版本。使用 -Os 它设法将我的代码放入 2K 程序内存 space.
代码如下:
#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;
}
不幸的是,逐行调试时,将其与第32页的test vectors in the implementation guide,“15 SPECK64/128 Test Vectors”进行比较,结果与预期结果不同。
调用此函数的方法如下:
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);
根据指南,"out" 的预期值应为 0x454e028b, 0x8c6fa548
。
我使用 -O2 得到的结果是 0x8FA3FED7 0x53D8CEA8
。
使用 -O1,我得到 0x454e028b, 0x8c6fa548
,这是正确的结果。
一步调试
实施指南包括所有中间键和其他值,所以我逐行执行代码,将结果与指南进行比较。
"x" 的预期结果是:03020100
、131d0309
、bbd80d53
、0d334df3
。我开始单步调试,但是当到达第 4 个结果 0d334df3
时,调试器 window 显示 0d334df0
。到下一轮,预期的 7fa43565
值为 7FA43578
并且每次迭代只会变得更糟。
这仅在启用 -O2 或更高版本时发生。在没有优化或使用 -O1 的情况下,代码按预期工作。
发布可编译的测试代码作为参考。
#include <stdint.h>
#include <string.h>
//#include "speck.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) {
for (uint32_t 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;
}
int main(void) {
uint32_t out[2];
uint32_t in[] = {0x7475432d, 0x3b726574};
uint32_t key[] = {0x03020100, 0x0b0a0908, 0x13121110, 0x1b1a1918};
encrypt_block(out, in, key);
printf("%8lx %8lx\n", (unsigned long) out[0], 0x454e028bLU);
printf("%8lx %8lx\n", (unsigned long) out[1], 0x8c6fa548LU);
}
输出
454e028b 454e028b
8c6fa548 8c6fa548
意外输出
0x8FA3FED7
0x53D8CEA8
我没有在您的代码中看到任何未定义行为的迹象,除非它在 setup/call 点的某些方面您没有显示。因此,行为应该不可能根据优化级别而有所不同。通常我不会很快将类似这样的事情归咎于编译器错误,而是针对嵌入式内容的 FOSS 编译器的分支,尤其是在并非针对 16- 设计的编译器中将 int
重新定义为 16 位的分支。位 int
,尤其是专有的分支,它们的代码非常糟糕,甚至不想让您看到它,编译器错误很有可能。
这是编译器中的错误。
我发布了问题 in the manufacturer's forum。其他人确实重现了这个问题,在编译某些部分时会发生这种情况。其他部分不受影响。
作为解决方法,我将宏更改为实际函数,并将操作分成两行:
uint32_t ROL(uint32_t x, uint8_t r) {
uint32_t intermedio;
intermedio = x << r;
intermedio |= x >> (32 - r);
return intermedio;
}
这给出了正确的结果。
我试图在 8 位 PIC 微控制器中安装 NSA SPECK 的实现。 他们的编译器(基于 CLANG)的免费版本不会启用优化,所以我 运行 内存不足。我尝试了启用 -O2、-O3 和 -Os(针对大小进行优化)的 "trial" 版本。使用 -Os 它设法将我的代码放入 2K 程序内存 space.
代码如下:
#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;
}
不幸的是,逐行调试时,将其与第32页的test vectors in the implementation guide,“15 SPECK64/128 Test Vectors”进行比较,结果与预期结果不同。
调用此函数的方法如下:
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);
根据指南,"out" 的预期值应为 0x454e028b, 0x8c6fa548
。
我使用 -O2 得到的结果是 0x8FA3FED7 0x53D8CEA8
。
使用 -O1,我得到 0x454e028b, 0x8c6fa548
,这是正确的结果。
一步调试
实施指南包括所有中间键和其他值,所以我逐行执行代码,将结果与指南进行比较。
"x" 的预期结果是:03020100
、131d0309
、bbd80d53
、0d334df3
。我开始单步调试,但是当到达第 4 个结果 0d334df3
时,调试器 window 显示 0d334df0
。到下一轮,预期的 7fa43565
值为 7FA43578
并且每次迭代只会变得更糟。
这仅在启用 -O2 或更高版本时发生。在没有优化或使用 -O1 的情况下,代码按预期工作。
发布可编译的测试代码作为参考。
#include <stdint.h>
#include <string.h>
//#include "speck.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) {
for (uint32_t 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;
}
int main(void) {
uint32_t out[2];
uint32_t in[] = {0x7475432d, 0x3b726574};
uint32_t key[] = {0x03020100, 0x0b0a0908, 0x13121110, 0x1b1a1918};
encrypt_block(out, in, key);
printf("%8lx %8lx\n", (unsigned long) out[0], 0x454e028bLU);
printf("%8lx %8lx\n", (unsigned long) out[1], 0x8c6fa548LU);
}
输出
454e028b 454e028b
8c6fa548 8c6fa548
意外输出
0x8FA3FED7
0x53D8CEA8
我没有在您的代码中看到任何未定义行为的迹象,除非它在 setup/call 点的某些方面您没有显示。因此,行为应该不可能根据优化级别而有所不同。通常我不会很快将类似这样的事情归咎于编译器错误,而是针对嵌入式内容的 FOSS 编译器的分支,尤其是在并非针对 16- 设计的编译器中将 int
重新定义为 16 位的分支。位 int
,尤其是专有的分支,它们的代码非常糟糕,甚至不想让您看到它,编译器错误很有可能。
这是编译器中的错误。
我发布了问题 in the manufacturer's forum。其他人确实重现了这个问题,在编译某些部分时会发生这种情况。其他部分不受影响。
作为解决方法,我将宏更改为实际函数,并将操作分成两行:
uint32_t ROL(uint32_t x, uint8_t r) {
uint32_t intermedio;
intermedio = x << r;
intermedio |= x >> (32 - r);
return intermedio;
}
这给出了正确的结果。