生成的代码与预期的扩展ASM不匹配
Generated code not matching expectations with Extended ASM
我有一个CpuFeatures
类。该类的要求很简单:(1)保留EBX
或RBX
,(2)在EAX/EBX/ECX/EDX
中记录CPUID
返回的值。我不确定生成的代码是我想要的代码。
CpuFeatures
类代码使用GCC扩展ASM。以下是相关代码:
struct CPUIDinfo
{
word32 EAX;
word32 EBX;
word32 ECX;
word32 EDX;
};
bool CpuId(word32 func, word32 subfunc, CPUIDinfo& info)
{
uintptr_t scratch;
__asm__ __volatile__ (
".att_syntax n"
#if defined(__x86_64__)
"t xchgq %%rbx, %q1 n"
#else
"t xchgl %%ebx, %k1 n"
#endif
"t cpuid n"
#if defined(__x86_64__)
"t xchgq %%rbx, %q1 n"
#else
"t xchgl %%ebx, %k1 n"
#endif
: "=a"(info.EAX), "=&r"(scratch), "=c"(info.ECX), "=d"(info.EDX)
: "a"(func), "c"(subfunc)
);
if(func == 0)
return !!info.EAX;
return true;
}
下面的代码是在Cygwin i386上用-g3 -Og
编译的。当我在调试器下检查它时,我不喜欢我所看到的。
Dump of assembler code for function CpuFeatures::DoDetectX86Features():
...
0x0048f355 <+1>: sub $0x48,%esp
=> 0x0048f358 <+4>: mov $0x0,%ecx
0x0048f35d <+9>: mov %ecx,%eax
0x0048f35f <+11>: xchg %ebx,%ebx
0x0048f361 <+13>: cpuid
0x0048f363 <+15>: xchg %ebx,%ebx
0x0048f365 <+17>: mov %eax,0x10(%esp)
0x0048f369 <+21>: mov %ecx,0x18(%esp)
0x0048f36d <+25>: mov %edx,0x1c(%esp)
0x0048f371 <+29>: mov %ebx,0x14(%esp)
0x0048f375 <+33>: test %eax,%eax
...
我不喜欢我所看到的,因为看起来EBX/RBX
是而不是被保存(xchg %ebx,%ebx
在+11
)。此外,看起来保存的EBX/RBX
被保存为CPUID
的结果,而不是CPUID
返回的EBX
的实际值(+15
时的xchg %ebx,%ebx
,在+29
时的mov %ebx,0x14(%esp)
之前)。
如果我将操作数更改为使用"=&m"(scratch)
的内存op,则生成的代码是:
0x0048f35e <+10>: xchg %ebx,0x40(%esp)
0x0048f362 <+14>: cpuid
0x0048f364 <+16>: xchg %ebx,0x40(%esp)
一个相关的问题是如何确保操作数的读/写在扩展ASM所需的时间发生?
我做错了什么(除了浪费了无数个小时的事情,应该花5或15分钟)?
下面的代码是一个完整的示例,我使用它来编译上面的示例代码,包括直接交换(swap)到info.EBX
变量的修改。
#include <inttypes.h>
#define word32 uint32_t
struct CPUIDinfo
{
word32 EAX;
word32 EBX;
word32 ECX;
word32 EDX;
};
bool CpuId(word32 func, word32 subfunc, CPUIDinfo& info)
{
__asm__ __volatile__ (
".att_syntax n"
#if defined(__x86_64__)
"t xchgq %%rbx, %q1 n"
#else
"t xchgl %%ebx, %k1 n"
#endif
"t cpuid n"
#if defined(__x86_64__)
"t xchgq %%rbx, %q1 n"
#else
"t xchgl %%ebx, %k1 n"
#endif
: "=a"(info.EAX), "=&m"(info.EBX), "=c"(info.ECX), "=d"(info.EDX)
: "a"(func), "c"(subfunc)
);
if(func == 0)
return !!info.EAX;
return true;
}
int main()
{
CPUIDinfo cpuInfo;
CpuId(1, 0, cpuInfo);
}
你应该做的第一个观察是我选择使用info。执行实际交换的EBX内存位置。这样就不需要另一个临时变量或寄存器了。
我用-g3 -Og -S -m32
汇编成32位代码,得到了这些感兴趣的指令:
xchgl %ebx, 4(%edi)
cpuid
xchgl %ebx, 4(%edi)
movl %eax, (%edi)
movl %ecx, 8(%edi)
movl %edx, 12(%edi)
%edi
恰好包含info
结构体的地址。4(%edi)
恰好是info.EBX
的地址。我们在cpuid
之后交换%ebx
和4(%edi)
。通过该指令,ebx
恢复到cpuid
之前的状态,4(%edi)
现在具有cpuid
执行后ebx
的状态。其余的movl
行通过%edi
寄存器将eax
、ecx
、edx
寄存器放入info
结构体的其余部分。
您的代码与scratch
变量(并使用约束"=&m"(scratch)
)永远不会在汇编器模板之后使用,因此%ebx,0x40(%esp)
有您想要的值,但它永远不会被移动到任何有用的地方。您必须将scratch
变量复制到info.EBX
(即。info.EBX = scratch;
)并查看生成的所有结果指令。在生成的汇编指令中,数据将从scratch
内存位置复制到info.EBX
。
Update - Cygwin and MinGW
我不完全满意Cygwin代码输出是正确的。半夜的时候,我恍然大悟!的时刻。当动态链接加载器加载图像(DLL等)并通过重基修改图像时,Windows已经完成了自己的位置无关代码。不需要像在Linux 32位共享库中那样进行额外的PIC处理,因此ebx
/rbx
没有问题。这就是为什么Cygwin和MinGW在使用-fPIC
警告:-fPIC忽略目标(所有代码与位置无关)
这是因为在Windows下,所有32位代码在被Windows动态加载器加载时都可以重新基于。在多布斯博士的文章中可以找到更多关于重新定位的信息。关于windows可移植可执行文件格式(PE)的信息可以在这篇Wiki文章中找到。Cygwin和MinGW在瞄准32位代码时不需要担心保留ebx
/rbx
,因为在他们的平台上PIC已经由操作系统,其他重基工具和链接器处理。
- 是否可以通过C++扩展强制多个python进程共享同一内存
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 如何将这个C++哈希表转换为动态扩展和收缩,而不是使用硬设置的最大值
- 扩展光电二极管探测器以支持多个传感器
- C++中的VLA,扩展名为std=C++11
- OpenGL 和 GLM 矩阵无法正确扩展,总是按比例缩小
- 基于范围的 for 循环:迭代使用一个元素扩展的向量
- C++返回 Numpy 数组的 Python 扩展模块
- 扩展可变参数模板中的变量名称
- 扩展C++生成的代码的模板参数类型名称
- 我想通过带有C++和Python的插件创建一个可扩展的应用程序
- VSCode IntelliSense无法识别SDL框架的SDL_image扩展库
- 将元组类型扩展为可变参数模板?
- 如何按文件扩展名引用文件夹中的文件
- HDF5Cpp 扩展复合数据集超板问题
- 使用C++获取程序的 ASM
- MSVC中的宏观扩展问题
- 通过GNU扩展asm约束加载64位整数常量
- 从Asm.js/emscripten启用WebGL扩展
- 生成的代码与预期的扩展ASM不匹配