编译器省略外部循环
Compiler omitting outer loop
我已经在一个C++项目中实现了(阅读:从wiki复制和粘贴(XXTEA密码。为了清楚起见,我将加密和解密分开在单独的函数中:(注意:这不是密码学问题!请不要评论所选密码(
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
static void btea_enc( unsigned int *v, unsigned n, const unsigned int* key ) {
unsigned int y, z, sum;
unsigned p, rounds, e;
rounds = 16 + 52/n;
sum = 0;
z = v[n-1];
do {
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++) {
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
} while (--rounds);
}
static void btea_dec( unsigned int *v, unsigned n, const unsigned int* key ) {
unsigned int y, z, sum;
unsigned p, rounds, e;
rounds = 16 + 52/n;
sum = rounds*DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--) {
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
} while ((sum -= DELTA) != 0);
}
#undef MX
#undef DELTA
当此代码在调试中编译时,它可以完美运行。但是,当我使用 Visual Studio 2013 (v120( 进行(默认(优化编译此代码时,btea_dec会丢失其外部循环(导致解密产生垃圾(。
用于加密和解密的反汇编列表。请注意解密过程中缺少的外部循环!(如果你想要代码作为文本,我很乐意上传,这只是一堵文字墙(
查看实际代码,结束条件是一个溢出的无符号 int 'sum':while ((sum -= DELTA) != 0)
我不明白编译器做了什么让它认为它可以摆脱这个循环(afaik 溢出只对整数未定义,无符号溢出完全没问题(。
问题:为什么编译器要"优化"外部循环?我该如何解决它?
MCVE:(在 include 和 main 之间粘贴包含 btea_enc 和 btea_dec 的上一个代码块(
#define _CRT_RAND_S
#include <cstdlib>
int main(int argc, char* argv[])
{
// Random key
unsigned int key[4];
rand_s(&key[0]);
rand_s(&key[1]);
rand_s(&key[2]);
rand_s(&key[3]);
// Buffer we'll be encrypting
unsigned int utext[4];
memcpy(utext, "SecretPlaintext", 16);
// Encrypt
btea_enc(utext, 4, key);
// Decrypt
btea_dec(utext, 4, key);
// Should still be equal!
bool s = !strcmp((char*)utext, "SecretPlaintext");
// Print message
printf("Compared: %sn", s ? "equal" : "falsly");
return s?0:1;
}
/GL,编译器知道n == 4
,因此rounds == 29
。 它肯定是预先计算sum
的初始值,这也是rounds*DELTA
。
接下来,它可能会尝试计算循环迭代次数并展开外循环。 如果它做错了(就像我在另一个答案中所做的那样(,它可能正在做uint32_t(rounds * DELTA) / DELTA
,这是一个。 添加第一次迭代作为do-while,这就是外循环的去向。
Gnasher 的循环控制代码对于编译器来说要容易得多,正好有 rounds
(29( 次迭代,它可能会也可能不会决定展开,但几乎没有空间来弄乱迭代次数。
为什么会发生这种情况超出了我的范围。您可以尝试更换
} while ((sum -= DELTA) != 0);
跟
sum -= DELTA;
} while ((--rounds) != 0);
第 1 步:宏是非常非常糟糕的编程风格。 用参数重写它,比如
#define MX(key,sum,p,e,y,z) (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
这样阅读您的代码的人实际上可以看到表达式中出现了哪些变量。
更好的是,使用内联函数。
编译器优化嵌套循环是正确的,因为可见的副作用是可预测的。
唯一可见的副作用是v[p] -= MX;
,这种情况发生次数可预测。 因此,编译器可以通过v[p] -= loopcount * MX;
替换嵌套循环
e
、z
和 y
被重复写入但从不读取,因此编译器可以完全消除它们及其计算。
请注意,对无能操作的这种优化可能会让您看到您认为已仔细消除的定时攻击。
整个函数体变成
int p=n;
int subtrahend = rounds * DELTA / DELTA * MX;
do {
v[--p] -= subtrahend;
} while (p);
- CMake 外部和内部静态库的循环依赖关系
- 全局变量的循环依赖性与外部说明符
- 自定义对象构造函数在循环外部循环
- C++:在循环内部或外部声明一个向量
- 在主循环外部多个文件上定义全局变量
- C++嵌套循环,外部有变量
- 如何在 for 循环内部或外部打印双精度变量
- C++:外部对象循环引用
- 与外部循环具有相同变量名称的内部循环
- 针对外部标志(cpp)的循环条件的更改
- 为什么用于解析我读取的行的内部循环每隔一次迭代就会通过外部读取循环跳过
- 用于循环通过未知大小的外部普通数组
- 在循环内声明变量,但在外部使用它
- 编译器省略外部循环
- 如何在没有外部标志的情况下只在循环中运行一次代码
- Apache Thrift外部事件循环
- 带有一个外部指针的链接节点的循环链
- 在对象外部循环遍历unique_ptr集合
- 如何调用外部c函数,而GLUT显示循环锁定了我的c++主函数
- OpenMP -嵌套for循环在外部循环之前并行时变得更快.为什么