我在这里读到,如果编译器memset知道传递的内存缓冲区永远不再使用,则可以自由删除调用.怎么可能?在我看来(从核心语言的角度来看)memset只是一个常规函数,编译器无权假设其中发生的任何事情都没有副作用.
在链接文章中,他们展示了如何删除Visual C++ 10 memset.我知道Microsoft编译器在标准合规性方面并不领先,所以我问 - 它是按照标准,还是仅仅是msvc-ism?如果符合标准,请详细说明;)
编辑: @Cubbi
以下代码:
void testIt(){
char foo[1234];
for (int i=0; i<1233; i++){
foo[i] = rand()%('Z'-'A'+1)+'A';
}
foo[1233]=0;
printf(foo);
memset(foo, 0, 1234);
}
Run Code Online (Sandbox Code Playgroud)
在mingw下用线编译:
g++ -c -O2 -frtti -fexceptions -mthreads -Wall -DUNICODE -o main.o main.cpp
g++ -Wl,-s -Wl,-subsystem,console -mthreads -o main.exe main.o
objdump -d -M intel -S main.exe > dump.asm
Run Code Online (Sandbox Code Playgroud)
给出输出:
4013b0: 55 push ebp
4013b1: 89 e5 mov ebp,esp
4013b3: 57 push edi
4013b4: 56 push esi
4013b5: 53 push ebx
4013b6: 81 ec fc 04 00 00 sub esp,0x4fc
4013bc: 31 db xor ebx,ebx
4013be: 8d b5 16 fb ff ff lea esi,[ebp-0x4ea]
4013c4: bf 1a 00 00 00 mov edi,0x1a
4013c9: 8d 76 00 lea esi,[esi+0x0]
4013cc: e8 6f 02 00 00 call 0x401640
4013d1: 99 cdq
4013d2: f7 ff idiv edi
4013d4: 83 c2 41 add edx,0x41
4013d7: 88 14 1e mov BYTE PTR [esi+ebx*1],dl
4013da: 43 inc ebx
4013db: 81 fb d1 04 00 00 cmp ebx,0x4d1
4013e1: 75 e9 jne 0x4013cc
4013e3: c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
4013e7: 89 34 24 mov DWORD PTR [esp],esi
4013ea: e8 59 02 00 00 call 0x401648
4013ef: 81 c4 fc 04 00 00 add esp,0x4fc
4013f5: 5b pop ebx
4013f6: 5e pop esi
4013f7: 5f pop edi
4013f8: c9 leave
4013f9: c3 ret
Run Code Online (Sandbox Code Playgroud)
在第4013ea行有memset调用,因此mingw没有删除它.由于mingw在Windows皮肤中真的是GCC,我想GCC也是这样做的 - 我会在重启到linux时检查它.
仍然无法找到这样的编译器?
EDIT2:
我刚刚发现了GCC的问题__attribute__ ((pure)).因此,不是编译器知道关于memset的特殊内容并且忽略了它,它只是允许它的头部 - 程序员使用它也应该看到它;)我的mingw在memset声明中没有这个属性,因此它不会从装配无论如何 - 正如我所料.我将不得不对此进行调查.
Dav*_*rtz 11
"编译器无权假设其中发生的任何事情都没有副作用."
那是对的.但是,如果实际上编译器知道什么实际发生的内部,并能够确定它真的有没有副作用,则不需要假设.
这几乎是所有编译器优化的工作方式.代码说"X".编译器确定如果"Y"为真,那么它可以用代码"Z"替换代码"X",并且没有可检测到的差异.它确定"Y"为真,然后用"Z"代替"X".
例如:
void func()
{
int j = 2;
foo();
if (j == 2) bar();
else baz();
}
Run Code Online (Sandbox Code Playgroud)
编译器可以优化它foo(); bar();.编译器可以看到foo无法合法修改的值j.如果foo()以某种方式神奇地找出j堆栈中的位置并修改它,那么优化将改变代码的行为,但这是程序员使用"魔法"的错误.
void func()
{
int j = 2;
foo(&j);
if (j == 2) bar();
else baz();
}
Run Code Online (Sandbox Code Playgroud)
现在它不能因为foo可以合法地修改j没有任何魔法的价值.(假设编译器无法查看内部foo,在某些情况下它可以.)
如果你做"魔术",那么编译器可以进行破坏代码的优化.坚持规则,不要使用魔法.
在您链接到的示例中,代码依赖于编译器打扰将特定值放在从未访问过的变量中并立即停止存在.编译器不需要执行任何对代码操作没有影响的操作.
可能影响代码的唯一方法是,如果它查看堆栈的未分配部分,或依赖于堆栈上具有以前具有的值的新分配.要求编译器这样做会使大量的优化变得不可能,包括用寄存器替换局部变量.
| 归档时间: |
|
| 查看次数: |
3950 次 |
| 最近记录: |