簡體   English   中英

可以通過編譯器刪除`memset`函數調用嗎?

[英]Can `memset` function call be removed by compiler?

我在這里讀到如果編譯器知道傳遞的內存緩沖區從未再次使用 ,那么編譯器可以自由地刪除對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);
}

在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

給出輸出:

 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   

在第4013ea行有memset調用,因此mingw沒有刪除它。 由於mingw在Windows皮膚中真的是GCC,我想GCC也是這樣做的 - 我會在我重啟到linux時檢查它。

仍然無法找到這樣的編譯器?

EDIT2:

我剛剛發現了GCC的__attribute__ ((pure)) 因此,不是編譯器知道關於memset的特殊內容並且忽略它,它只是允許它在它的頭部 - 使用它的程序員也應該看到它;)我的mingw在memset聲明中沒有這個屬性,因此它不會從大會無論如何 - 正如我所料。 我將不得不對此進行調查。

“編譯器無權假設其中發生的任何事情都沒有副作用。”

那是對的。 但是,如果實際上編譯器知道到底發生了什么里面,並且能夠確定它真的沒有副作用,則不需要假設。

這幾乎是所有編譯器優化的工作方式。 代碼說“X”。 編譯器確定如果“Y”為真,那么它可以用代碼“Z”替換代碼“X”,並且沒有可檢測到的差異。 它確定“Y”為真,然后用“Z”代替“X”。

例如:

void func()
{
  int j = 2;
  foo();
  if (j == 2) bar();
   else baz();
}

編譯器可以將其優化為foo(); bar(); foo(); bar(); 編譯器可以看到foo不能合法地修改j的值。 如果foo()以某種方式神奇地指出j在堆棧中的位置並修改它,那么優化將改變代碼的行為,但這是程序員使用“魔術”的錯誤。

void func()
{
  int j = 2;
  foo(&j);
  if (j == 2) bar();
   else baz();
}

現在它不能,因為foo可以在沒有任何魔法的情況下合法地修改j的值。 (假設編譯器無法查看foo內部,在某些情況下它可以。)

如果你做“魔術”,那么編譯器可以進行破壞代碼的優化。 堅持規則,不要使用魔法。

在您鏈接到的示例中,代碼依賴於編譯器打擾將特定值放在從未訪問過的變量中並立即停止存在。 編譯器不需要執行任何對代碼操作沒有影響的操作。

可能影響代碼的唯一方法是,如果它查看堆棧的未分配部分,或依賴於堆棧上具有以前具有的值的新分配。 要求編譯器這樣做會使大量的優化變得不可能,包括用寄存器替換局部變量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM