gui*_*low 49 c c++ optimization endianness strict-aliasing
我们最近在大学里开了一个关于多种语言编程特色的讲座.
讲师写下了以下功能:
inline u64 Swap_64(u64 x)
{
u64 tmp;
(*(u32*)&tmp) = Swap_32(*(((u32*)&x)+1));
(*(((u32*)&tmp)+1)) = Swap_32(*(u32*) &x);
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
虽然我完全理解这在可读性方面也是非常差的风格,但他的主要观点是这部分代码在生产代码中运行良好,直到它们实现了高优化级别.然后,代码将什么都不做.
他说,变量的所有赋值tmp都将由编译器优化.但为什么会这样呢?
我知道有些情况下变量需要声明为volatile,这样编译器就不会触及它们,即使他认为它们永远不会被读或写,但我不知道为什么会发生这种情况.
Jar*_*d42 47
在C++中,如果指针参数char*指向基本上不同的类型("严格别名"规则),则假定它们不是别名(除外).这允许一些优化.
在这里,u64 tmp永远不会被修改为u64.
的内容u32*被修改,而可以是无关的" u64 tmp"这样可以被看作nop用于u64 tmp.
Sha*_*our 41
此代码违反了严格的别名规则,这使得通过不同类型的指针访问对象是非法的,尽管允许通过*char**进行访问.允许编译器假设不同类型的指针不指向相同的存储器并相应地进行优化.它还意味着代码调用未定义的行为,并且可以真正做任何事情.
这个主题的最佳参考之一是理解严格别名,我们可以看到第一个例子与OP的代码类似:
uint32_t swap_words( uint32_t arg )
{
uint16_t* const sp = (uint16_t*)&arg;
uint16_t hi = sp[0];
uint16_t lo = sp[1];
sp[1] = hi;
sp[0] = lo;
return (arg);
}
Run Code Online (Sandbox Code Playgroud)
文章解释说这段代码违反了严格的别名规则,因为sp它是别名,arg但是它们有不同的类型,并说尽管它会编译,但它很可能arg在swap_words返回后没有变化.虽然通过简单的测试,我无法使用上面的代码和OP代码重现该结果,但这并不意味着什么,因为这是未定义的行为,因此无法预测.
本文接着讨论了许多不同的案例,并提出了几种工作解决方案,包括通过union进行类型惩罚,这在C99 1中有明确定义,可能在C++中未定义,但在实践中得到大多数主要编译器的支持,例如这里是gcc关于类型惩罚的参考.前面的线程C和C++中的联盟目的进入了血腥的细节.虽然这个主题有很多主题,但这似乎做得最好.
该解决方案的代码如下:
typedef union
{
uint32_t u32;
uint16_t u16[2];
} U32;
uint32_t swap_words( uint32_t arg )
{
U32 in;
uint16_t lo;
uint16_t hi;
in.u32 = arg;
hi = in.u16[0];
lo = in.u16[1];
in.u16[0] = lo;
in.u16[1] = hi;
return (in.u32);
}
Run Code Online (Sandbox Code Playgroud)
作为参考,关于严格混叠的C99标准草案的相关部分是表达式第7段,其中说:6.5
对象的存储值只能由具有以下类型之一的左值表达式访问:76)
- 与对象的有效类型兼容的类型,
- 与对象的有效类型兼容的类型的限定版本,
- 对应于对象的有效类型的有符号或无符号类型,
- 对应于对象有效类型的限定版本的有符号或无符号类型,
- 聚合或联合类型,其成员中包含上述类型之一(包括递归地,子聚合或包含联合的成员),或者
- 角色类型.
和脚注76说:
此列表的目的是指定对象可能或可能没有别名的情况.
C++草案标准的相关部分是3.10 Lvalues和rvalues第10段
文章Type-punning和strict-aliasing给出了一个更温和但不太完整的主题介绍,重新访问的C99给出了对C99和别名的深入分析,而不是轻松阅读.这个访问非活动联盟成员的答案 - 未定义?通过C++中的联合来讨论类型惩罚的泥泞细节,也不是轻松阅读.
脚注:
pep*_*ico 10
g ++(Ubuntu/Linaro 4.8.1-10ubuntu9)4.8.1:
> g++ -Wall -std=c++11 -O0 -o sample sample.cpp
> g++ -Wall -std=c++11 -O3 -o sample sample.cpp
sample.cpp: In function ‘uint64_t Swap_64(uint64_t)’:
sample.cpp:10:19: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
(*(uint32_t*)&tmp) = Swap_32(*(((uint32_t*)&x)+1));
^
sample.cpp:11:54: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
(*(((uint32_t*)&tmp)+1)) = Swap_32(*(uint32_t*) &x);
^
Run Code Online (Sandbox Code Playgroud)
Clang 3.4没有在任何优化级别发出警告,这很奇怪 ......
| 归档时间: |
|
| 查看次数: |
4343 次 |
| 最近记录: |