![](/img/trans.png)
[英]Why does my compiler use an 8-bit char when I'm running on a 64-bit machine?
[英]Why does 64-bit VC++ compiler add nop instruction after function calls?
我使用Visual Studio C ++ 2008 SP1, x64
C++
编译器编译了以下内容:
我很好奇,为什么编译器会在那些call
之后添加那些nop
指令?
PS1。 我会理解第二和第三个nop
将是4字节边距上的代码对齐,但第一个nop
打破了这个假设。
PS2。 编译的C ++代码中没有循环或特殊的优化内容:
CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTestDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
//This makes no sense. I used it to set a debugger breakpoint
::GdiFlush();
srand(::GetTickCount());
}
PS3。 附加信息: 首先,谢谢大家的意见。
以下是其他观察结果:
我的第一个猜测是增量链接可能与它有关。 但是,项目的Visual Studio
的Release
构建设置具有incremental linking
。
这似乎只影响x64
版本。 构建为x86
(或Win32
)的相同代码没有那些nop
,即使使用的指令非常相似:
VS 2013
生成的x64
代码看起来有些不同,它仍会在一些call
之后添加那些nop
: dynamic
与static
链接到MFC也没有区别存在那些nop
。 这个与VS 2013
动态链接到MFC dll: nop
S能后出现near
及far
call
S作为很好,他们什么都没有做比对。 以下是我从IDA
获得的代码的一部分,如果我再进一步说明: 如您所见,在far
call
之后插入nop
,恰好“对齐” B
地址上的下一个lea
指令! 如果仅为了对齐而添加这些内容毫无意义。
near
relative
call
(即那些以E8
开头的call
)比far
call
s(或以FF
开头的那些,在这种情况下为15
) 更快一些 链接器可能首先尝试near
call
s,并且因为它们比far
call
s短一个字节,如果成功,它可以在末尾用nop
s填充剩余空间。 但是上面的例子(5)有点打败了这个假设。
所以我仍然没有明确的答案。
这纯粹是猜测,但它可能是某种SEH优化。 我说优化是因为SEH似乎在没有NOP的情况下工作正常。 NOP可能有助于加速平仓。
在下面的示例中( 使用VC2017进行实时演示 ),在test1
调用basic_string::assign
后插入了NOP
,但在test2
没有(相同但声明为非抛出1 )。
#include <stdio.h>
#include <string>
int test1() {
std::string s = "a"; // NOP insterted here
s += getchar();
return (int)s.length();
}
int test2() throw() {
std::string s = "a";
s += getchar();
return (int)s.length();
}
int main()
{
return test1() + test2();
}
部件:
test1:
. . .
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign
npad 1 ; nop
call getchar
. . .
test2:
. . .
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign
call getchar
请注意,MSVS默认使用/EHsc
标志进行编译(同步异常处理)。 如果没有那个标志, NOP
消失,并且使用/EHa
(同步和异步异常处理), throw()
不再/EHa
,因为SEH始终打开。
1由于某些原因,只有throw()
似乎减少了代码大小,使用noexcept
使生成的代码更大并且召唤更多的NOP
。 MSVC ...
这是一个特殊的填充程序,让异常处理程序/展开函数正确检测它是否是函数的序言/结尾/正文。
这是由于x64中的调用约定要求堆栈在任何调用指令之前对齐16字节。 这不是(我的知识)硬件要求,而是软件要求。 这提供了一种方法来确保在进入函数时(即,在调用指令之后),堆栈指针的值总是8模16。因此允许从堆栈中的对齐位置进行简单的数据对齐和存储/读取。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.