GCC中的奇怪优化(删除函数)
我在GCC4.8.2中遇到了一个奇怪的优化,它使用-O2删除了一个函数 请参阅下面的代码 increase()和increase2()相同,只是后者中有一个printf() 但是,如果在GCC中使用-O2,则会删除increase()GCC中的奇怪优化(删除函数),c,gcc,optimization,C,Gcc,Optimization,我在GCC4.8.2中遇到了一个奇怪的优化,它使用-O2删除了一个函数 请参阅下面的代码 increase()和increase2()相同,只是后者中有一个printf() 但是,如果在GCC中使用-O2,则会删除increase() #include <stdio.h> #include <stdint.h> #include <arpa/inet.h> void swap(uint64_t *vector) { uint32_t *p = (ui
#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
void swap(uint64_t *vector)
{
uint32_t *p = (uint32_t *)vector;
uint32_t tmp = p[0];
p[0] = htonl(p[1]);
p[1] = htonl(tmp);
}
void increase(uint64_t *vector)
{
swap(vector);
(*vector)++;
swap(vector);
}
void increase2(uint64_t *vector)
{
swap(vector);
(*vector)++;
printf("touch...\n");
swap(vector);
}
int main()
{
uint64_t vector = 0xa;
increase(&vector);
printf("%lx\n", vector);
increase2(&vector);
printf("%lx\n", vector);
return 1;
}
为什么?
提前谢谢
对不起,我没有说清楚 问题不是“删除函数”,而是“函数不影响参数” Matt McNabb指出了原因,严格的别名规则。
非常感谢。如果函数实际被删除,您的输出将更像:
touch...
a
touch...
10000000000000a
为了回答你的问题,它正在打印这个,因为你告诉它按那个顺序打印这三行
对increase()
的调用不会输出任何内容,然后使用printf(“%lx\n”,vector)
输出a
然后,调用increase2()
并按设计输出touch…
,然后使用printf(“%lx\n”,vector)输出100000000000a
您是否检查了未优化的输出?如果函数实际被删除,您的输出将更像:
touch...
a
touch...
10000000000000a
为了回答你的问题,它正在打印这个,因为你告诉它按那个顺序打印这三行
对increase()
的调用不会输出任何内容,然后使用printf(“%lx\n”,vector)
输出a
然后,调用increase2()
并按设计输出touch…
,然后使用printf(“%lx\n”,vector)输出100000000000a
是否检查了未优化的输出?此代码导致未定义的行为:
uint32_t *p = (uint32_t *)vector;
uint32_t tmp = p[0];
vector
指向的内存是类型为uint64\u t
的对象,但是您可以通过类型为uint32\u t
的左值读取它。这违反了严格的别名规则
由于您的程序总是调用此函数,因此整个程序的行为是未定义的。因此,当优化器切断通向UB的路径时,您可能会看到奇怪的优化工件
另一个问题是,您正在使用%lx
打印uint64\u t
。除非您的系统使用64位长,否则这将无法工作。要获得正确的说明符,请使用printf(“%”PRIx64“\n”,vector)代码>。为此,您可能需要#包括
在修复printf格式说明符后,我的系统在-O2
和更低的位置提供以下信息,或者如果使用-fno strict aliasing
开关:
10000000000000a
touch...
20000000000000a
和垃圾在-O3
此代码导致未定义的行为:
uint32_t *p = (uint32_t *)vector;
uint32_t tmp = p[0];
vector
指向的内存是类型为uint64\u t
的对象,但是您可以通过类型为uint32\u t
的左值读取它。这违反了严格的别名规则
由于您的程序总是调用此函数,因此整个程序的行为是未定义的。因此,当优化器切断通向UB的路径时,您可能会看到奇怪的优化工件
另一个问题是,您正在使用%lx
打印uint64\u t
。除非您的系统使用64位长,否则这将无法工作。要获得正确的说明符,请使用printf(“%”PRIx64“\n”,vector)代码>。为此,您可能需要#包括
在修复printf格式说明符后,我的系统在-O2
和更低的位置提供以下信息,或者如果使用-fno strict aliasing
开关:
10000000000000a
touch...
20000000000000a
和垃圾在-O3
我知道你的问题是“为什么”。马特·麦克纳布解释得很好。但如何修复呢
通常编译器和CPU更喜欢值而不是指针,因为它们有更多(更安全)的优化空间。因此,让我们完全避免指针技巧,只进行值转换:
#include <endian.h>
void swap(uint64_t *vector)
{
*vector = htobe64(le64toh(*vector));
}
#包括
无效交换(uint64_t*向量)
{
*向量=htobe64(le64toh(*向量));
}
这使您的程序行为对优化不敏感。我知道您的问题是“为什么”。马特·麦克纳布解释得很好。但如何修复呢
通常编译器和CPU更喜欢值而不是指针,因为它们有更多(更安全)的优化空间。因此,让我们完全避免指针技巧,只进行值转换:
#include <endian.h>
void swap(uint64_t *vector)
{
*vector = htobe64(le64toh(*vector));
}
#包括
无效交换(uint64_t*向量)
{
*向量=htobe64(le64toh(*向量));
}
这使您的程序行为对优化不敏感。我看不出您描述的问题。似乎正在调用increase()
和increase2()
。。。问题是什么?学习如何使用gnu调试器一步一步地查看执行情况。@Wedapasi老实说,对初学者来说,调试优化后的代码会非常混乱。在这种特定情况下,即使是main
操作也会重新排序。另一方面,当然知道gdb
是合理的。你能检查一下你实际上正在运行这段代码吗?比如说,在每次调用increase
之前,你没有打印printf
?@dlask:非常正确。我完全同意你的意见。但是,我怀疑这是否会成为这个特定代码的一个问题,因为输出是预期的、未定义的行为。我个人认为,看到步骤viz代码的执行和实际调试之间有细微的区别。查看步骤viz代码的执行可以真正帮助理解代码流,不一定是在出现问题的情况下。我没有看到您描述的问题。似乎正在调用increase()
和increase2()
。。。问题是什么?学习如何使用gnu调试器一步一步地查看执行情况。@Wedapasi老实说,对初学者来说,调试优化后的代码会非常混乱。在这种特定情况下,即使是main
操作也会重新排序。另一方面,房委会