簡體   English   中英

Windows 中 asm("nop") 的實現

[英]Implementations for asm("nop") in windows

以分號結尾的空代碼行是否等同於 asm("nop") 指令?

volatile int x = 5;

if(x == 5){
  printf("x has not been changed yet\n");
}
else{
  ;//Is this the same as asm("nop") or __asm nop in windows?
  //alternatively could use __asm nop or __nop();
}

我看了這個答案,它讓我不想使用使用內聯匯編的 x86 特定實現。 `__asm nop` 是 GCC 編譯器中 `asm volatile("nop");` 的 Windows 等價物嗎?

我可以使用這個 void __nop(); msdn 似乎推薦的函數,但如果我不需要,我不想拖入庫中。 https://docs.microsoft.com/en-us/cpp/intrinsics/nop?view=vs-2017

有沒有一種廉價、便攜的方法來添加不會被編譯出來的 nop 指令? 我認為一個空的分號要么是 nop 要么是編譯出來的,但由於某種原因我今晚找不到任何關於它的信息。

澄清編輯我可以使用內聯 asm 為 x86 執行此操作,但我希望它是可移植的。 我可以使用 Windows 庫 __nop() 但我不想將該庫導入到我的項目中,這是不需要的開銷。

我正在尋找一種切割方式來生成不會被優化的 NOP 指令(最好使用標准 C 語法),該指令可以制成 MACRO 並在整個項目中使用,具有最小的開銷和工作(或者可以很容易地改進為工作)在 windows/linux/x86/x64 上。

謝謝。

我的意思是我不想添加一個庫只是為了強制編譯器添加一個 NOP。

... 以一種獨立於編譯器設置(例如優化設置)的方式和一種適用於所有 Visual C++ 版本(甚至可能是其他編譯器)的方式:

沒有機會:只要匯編代碼具有 C 代碼所描述的行為,編譯器就可以自由決定如何生成代碼。

而且因為NOP指令不會改變程序的行為,編譯器可以隨意添加或刪除它。

即使您找到了強制編譯器生成NOP :編譯器的一次更新或 Windows 更新修改某個文件,編譯器可能不再生成NOP指令。

我可以使用內聯 asm 為 x86 執行此操作,但我希望它是可移植的。

正如我上面寫的,任何強制編譯器編寫NOP都只能在特定 CPU 的特定編譯器版本上工作。

使用內聯匯編或__nop()您可能涵蓋某個制造商的所有編譯器(例如:所有 GNU C 編譯器或 Visual C++ 的所有變體等......)。

另一個問題是:您是否明確需要“官方” NOP指令,還是可以接受任何無所作為的指令?

如果您可以接受任何(幾乎)什么都不做的指令,那么讀取全局或靜態volatile變量可以替代NOP

static volatile char dummy;
    ...
else
{
    (void)dummy;
}

這應該強制編譯器添加讀取變量dummyMOV指令。

背景:

如果您編寫了設備驅動程序,則可以將變量dummy鏈接到讀取變量具有“副作用”的某個位置。 示例:讀取位於 VGA 視頻內存中的變量會影響屏幕內容!

使用volatile關鍵字不僅告訴編譯器變量的值可能隨時更改,而且讀取變量可能會產生這樣的影響。

這意味着編譯器必須假設不讀取變量會導致程序無法正常工作。 它無法優化掉(實際上不必要的) MOV指令讀取變量。

以分號結尾的空代碼行是否等同於 asm("nop") 指令?

不,當然不是。 你可以自己嘗試一下。 (在您自己的機器上,或在 Godbolt 編譯器瀏覽器上, https: //godbolt.org/)

如果FOO(x);你不希望無辜的 CPP 宏引入 NOP FOO(x); 擴展到只是; 因為在這種情況下FOO()的適當定義是空字符串。


__nop()不是庫函數。 它是一種內在的,可以完全滿足您的需求。 例如

#ifdef USE_NOP

#ifdef _MSC_VER
#include <intrin.h>
#define NOP() __nop()       // _emit 0x90
#else
// assume __GNUC__ inline asm
#define NOP() asm("nop")    // implicitly volatile
#endif

#else
#define NOP()  // no NOPs
#endif

int idx(int *arr, int b) {
    NOP();
    return arr[b];
}

使用 Clang7.0 -O3 for x86-64 Linux 編譯到這個 asm

idx(int*, int):
    nop
    movsxd  rax, esi                     # sign extend b
    mov     eax, dword ptr [rdi + 4*rax]
    ret

使用 32 位 x86 MSVC 19.16 -O2 -Gv 編譯到這個 asm

int idx(int *,int) PROC                                    ; idx, COMDAT
    npad    1                           ; pad with a 1 byte NOP
    mov     eax, DWORD PTR [ecx+edx*4]  ; __vectorcall arg regs
    ret     0

並使用 x64 MSVC 19.16 -O2 -Gv 編譯到這個 asm 所有這些都是 Godbolt

int idx(int *,int) PROC                             ; idx, COMDAT
    movsxd  rax, edx
    npad    1                           ; pad with a 1 byte NOP
    mov     eax, DWORD PTR [rcx+rax*4]  ; x64 __vectorcall arg regs
    ret     0

有趣的是, b到 64 位的符號擴展是在 NOP 之前完成的。 顯然,x64 MSVC 要求(默認情況下)函數以至少 2 字節或更長的指令開頭(在 1 字節push指令的序言之后,也許?),因此它們支持使用jmp rel8熱修補。


如果你在 1 指令函數中使用它,你會在x64 MSVCnpad 1之前得到一個npad 2 (2 字節 NOP)

int bar(int a, int b) {
    __nop();
    return a+b;
}
;; x64 MSVC 19.16
int bar(int,int) PROC                                  ; bar, COMDAT
    npad    2
    npad    1
    lea     eax, DWORD PTR [rcx+rdx]
    ret     0

我不確定 MSVC 將如何積極地對純寄存器指令重新排序 NOP,但是a^=b; __nop()之后實際上會在 NOP 指令之前導致xor ecx, edx

但是。 內存訪問,在這種情況下,MSVC 決定不重新排序任何東西來填充那個 2 字節的插槽。

int sink;
int foo(int a, int b) {
    __nop();
    sink = 1;
    //a^=b;
    return a+b;
}
;; MSVC 19.16 -O2
int foo(int,int) PROC                                  ; foo, COMDAT
    npad    2
    npad    1
    lea     eax, DWORD PTR [rcx+rdx]
    mov     DWORD PTR int sink, 1             ; sink
    ret     0

它首先執行 LEA,但不會在__nop()之前移動它; 似乎明顯錯過了優化,但是如果您要插入__nop()指令,那么優化顯然不是優先事項。


如果您編譯為.obj.exe並反匯編,您會看到一個普通的0x90 nop 但不幸的是,Godbolt 不支持 MSVC,只支持 Linux 編譯器,所以我能輕松做的就是復制 asm 文本輸出。

正如您所期望的那樣,如果定義了__nop() ,函數將正常編譯為相同的代碼,但沒有npad指令。


nop指令將與 NOP() 宏在 C 抽象機中運行的次數一樣多。 訂購wrt。 優化器或wrt不保證周圍的非易失volatile內存訪問。 寄存器中的計算。

如果您希望它成為編譯時內存重新排序的障礙,對於 GNU C,請使用 asm("nop" ::: "memory");`。 對於 MSVC,我認為這必須是分開的。

暫無
暫無

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

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