是一条空的代码线,结尾为半隆等于ASM(" NOP")指令?
volatile int x = 5;
if(x == 5){
printf("x has not been changed yetn");
}
else{
;//Is this the same as asm("nop") or __asm nop in windows?
//alternatively could use __asm nop or __nop();
}
我查看了这个答案,这使我不想使用X86特定的使用Inline Assembly实现。是`______
我可以使用此void __nop();MSDN似乎推荐了功能,但是如果我不需要,我不想拖入图书馆。https://learn.microsoft.com/en-us/cpp/intrinsics/nop?view=vs-2017
是否有一种便宜的,便携式的方式来添加不会被编译的NOP指令?我认为一个空的半龙要么是nop的,要么被编译出来,但由于某种原因,我今晚找不到任何信息。
澄清编辑我可以使用内联ASM对X86执行此操作,但我希望它可以便携。我可以使用Windows Library __nop(),但我不想将库将其导入我的项目。
我正在寻找一种生成不优化的NOP指令(最好使用标准C语法)生成的方法,该指令可以将其制成宏,并在整个项目中使用,在开销中具有最小的高架和工作(或可以轻松工作)在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;
}
这应该迫使编译器添加MOV
指令读取变量dummy
。
背景:
如果您写了一个设备驱动程序,则可以将变量dummy
链接到某些位置,其中读取变量具有"副作用"。示例:读取位于VGA视频内存中的变量可能会导致屏幕内容!
使用volatile
关键字,您不仅告诉编译器,该变量的值可能随时更改,而且读取变量可能会产生此类效果。
这意味着编译器必须假定不读取变量会导致程序无法正常工作。它无法优化(实际上是不必要的)MOV
读取变量的指令。
是一条空的代码线,以与ASM(" NOP")指令等效的半隆结尾?
不,当然不是。您本可以自己尝试。(在您自己的机器上,或在Godbolt编译器探险器上,https://godbolt.org/)
如果FOO(x);
扩展到;
,您不希望无辜的CPP宏来引入NOP,因为在这种情况下,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编译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 to to to asm (所有这些):
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
有趣的是,在NOP之前完成了b
至64位的签名。显然,X64 MSVC(默认情况下)函数至少以2字节或更长的说明开头(在1字节push
指令之后,也许是?),因此它们支持jmp rel8
。
如果您在1个Instruction函数中使用它,则在x64 msvc的npad 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()
之后实际上会导致 xor ecx, edx
nop指令。
但是WRT。在这种情况下
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()
IFDEFEF,正常函数将功能汇编为相同的代码,但没有npad
指令。
nop
指令将与NOP()宏在C抽象计算机中一样多次运行。订购WRT。围绕非volatile
内存访问并不能由Optimizer或WRT保证。寄存器中的计算。
如果您希望它是编译时内存的重新排序障碍,则使用ASM(" nop" :::" memory");``。对于MSVC,我认为这必须是分开的。