chr*_*nte 6 c++ optimization language-lawyer
考虑以下程序:
struct X {
bool b;
int y;
};
void f(int*);
bool test(X x) {
if (!x.b) {
return false;
}
f(&x.y);
return x.b;
}
Run Code Online (Sandbox Code Playgroud)
看来声明
return x.b;
Run Code Online (Sandbox Code Playgroud)
没有得到优化return true;
所以我的结论是,编译器必须假设可以通过这样做进行f(int*)修改:x.b
void f(int* p) {
bool* q = reinterpret_cast<bool*>(p - 1);
*q = false;
}
Run Code Online (Sandbox Code Playgroud)
f这是真正有效的 C++实现吗?或者换句话说,当指向成员的指针传递给不透明函数时,编译器是否必须始终假设整个对象可能会发生变化?
带有 clang-x86 trunk (-O1) 的编译器输出是这样的:
test(X): # @test(X)
push rax
mov qword ptr [rsp], rdi
test dil, dil
je .LBB0_1
lea rdi, [rsp + 4]
call f(int*)@PLT
cmp byte ptr [rsp], 0
setne al
pop rcx
ret
.LBB0_1:
xor eax, eax
pop rcx
ret
Run Code Online (Sandbox Code Playgroud)
我能想到的一种解释是,f如果我只像上面的示例中那样调用它,则给定的定义是有效的 C++,因此编译器必须假设这种可能性。
显示的实现f肯定是未定义的行为。然而,编译器不仅要遵守 C++ 语言,还要遵守允许更改的特定于平台的行为x.b。
编译器看不到是如何f实现的。如果用汇编写呢?这样的实现当然可以做它想做的事,包括在x.b不违反任何规范和标准的情况下进行更改。因此,它必须假设最坏的情况并重新读取x.b。
虽然所示的实现f会导致未定义的行为,但可以以正确的方式实现:
#include <type_traits>
#include <cstddef>
void f(int* p)
{
static_assert(::std::is_standard_layout_v<X>);
X * p_object{reinterpret_cast<X *>(reinterpret_cast<unsigned char *>(p) - offsetof(X, y))};
p_object->b = false;
}
Run Code Online (Sandbox Code Playgroud)
因此编译器可能会假设对标准布局类型的对象的任何字段进行别名会导致整个对象的别名。
| 归档时间: |
|
| 查看次数: |
134 次 |
| 最近记录: |