内联 asm 到 x64 - 理解
Posted
技术标签:
【中文标题】内联 asm 到 x64 - 理解【英文标题】:inline asm to x64 - understanding 【发布时间】:2016-01-15 19:42:50 【问题描述】:需要一些帮助来理解这个内联 asm 的作用 (c++),以便我可以正确转换它,以便我可以为 x64 编译。 Visual Studio 和 x64 无法使用内联汇编。我的研究告诉我,我应该使用 intrinics 或将内联 asm 放入 asm 文件并将该 asm 文件放入我的项目中。
如你所知,我真的不知道我在说什么,但你应该明白一般的想法。
我真的很想了解如何让这个内联汇编在 x86 或 x64 编译代码中工作。如果我能理解这是在做什么,那么也许我可以转换为 C++。或将此内联 asm 移动到 asm 文件。我有点知道如何移动到一个单独的文件。移动到 asm 文件的问题是我不确定如何设置我的 asm 函数来接受我需要传递给它的参数。我想要一个简单的答案,有人只是为我做这件事,但我需要的是一个解释,以便我将来可以自己做。我有两个内联 asm 块,这似乎是两者中更容易的一个。
inline asm to asm file
// FIRST inline asm talked about above
// little endian
void BlockInc(unsigned char *data)
#if DATA_BLOCK_SIZE==16
__asm
mov edi,[data]
add dword ptr [edi+0],1
adc dword ptr [edi+4],0
adc dword ptr [edi+8],0
adc dword ptr [edi+12],0
#else
#error
#endif
//this is the second more difficult inline asm not mentioned
void concThread(void *param)
unsigned long threadN = *((unsigned long *)param);
while(true)
unsigned long index;
// 1. synch
WaitForSingleObject(concThread_semaphores1[threadN],INFINITE);
ReleaseSemaphore(concThread_semaphores2[threadN],1,NULL);
if(sharedStop)
_endthread();
// start "sharedValue" concurrent modify
while(!sharedOkToGo);
for(index=0;index<(CYCLE_NUM/128);index++)
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
if(threadN%2)
__asm
movzx ecx,byte ptr [li]
jecxz LOOP0_PREHEAD
jmp LOOP0_HEAD
LOOP0_PREHEAD:
inc ecx
LOOP0_HEAD:
dec sharedValue
loop LOOP0_HEAD
else
__asm
movzx ecx,byte ptr [li]
jecxz LOOP1_PREHEAD
jmp LOOP1_HEAD
LOOP1_PREHEAD:
inc ecx
LOOP1_HEAD:
inc sharedValue
loop LOOP1_HEAD
ReleaseSemaphore(concThread_semaphores2[threadN],1,NULL);
EDITED asm:下面的评论表明它没有正确编写。
【问题讨论】:
这是不建议 x86 使用内联汇编且 x64 或 ARM 不支持内联汇编的原因之一。编写此代码是为了针对某些可能比 C++ 更快的古老平台进行优化。 你能建议一种方法来完成同样的事情,但使用 c++ 吗? 我怀疑这个组件的第二个块对于任何平台来说都是最佳的,而且它根本不清楚是否有任何有用的东西。您需要弄清楚它应该做什么以及它是否真的有用,完全重写它以根据需要使用原子操作。 【参考方案1】:对于初学者来说,代码是错误的。鉴于inc
没有修改进位标志,下面的adc
将使用它的初始零值,因此它永远不会进位到第二个双字。
也就是说,如果你想自己转换这段代码,你当然需要一个指令集引用来查看每条指令的作用。在这种情况下,显然它想增加一个 128 位的数字。
【讨论】:
如何修改此代码以按照您建议的方式实际工作?我能够将其与内联文件分离为 asm 文件,但我的 x64 重写并不顺利。我稍后会发布! 您应该使用add dword ptr [edi+ 0], 1
而不是inc dword ptr [edi+ 0]
,您可以删除clc
。独立的 64 位版本只有 3 条指令:add qword ptr [rcx], 1; adc qword ptr [rcx+8], 0; ret
。
.code BlockIncrement PROC, var1:QWORD mov rdi,var1 add qword ptr [rcx],1 adc qword ptr [rcx+8],0 ret BlockIncrement ENDP END【参考方案2】:
这个
movzx ecx,byte ptr [li]
jecxz LOOP0_PREHEAD
jmp LOOP0_HEAD
LOOP0_PREHEAD:
inc ecx
LOOP0_HEAD:
dec sharedValue
loop LOOP0_HEAD
看起来像
// ecx was used to hold a loop count
uint32_t i = li;
// do-while to guarantee execution at least once
// since LOOP0_PREHEAD incremented ecx if it was zero
do
sharedValue--;
i--;
while (i > 0);
【讨论】:
由于代码看起来像是在对访问同一内存位置的多个线程(sharedValue
)进行某种测试/验证,我相信原始 asm 的目的是确保内存访问不会重新排序、延迟或优化。在 MSVC++ 中,通过 volatile
引用访问 sharedValue
可能会给出类似的结果。他们也可能希望确保sharedValue
使用dec
和inc
更新,它们提供了一些原子性。使用 C 运算符可能不会在机器级别映射到 inc
/dec
。
@MichaelBurr 如果std::atomic
可用,std::atomic<int64_t> sharedValue;
然后sharedValue.fetch_sub(1);
可能更可取?
当然 - 我应该想到提一下。
是的,最后一位 inline 正在测试多个线程。我不知道它到底是怎么做的,因为我根本不懂 asm。内联代码在使用线程进行某种验证的代码中。你的描述听起来很可能。
我可以删除这个内联并替换为 do while?以上是关于内联 asm 到 x64 - 理解的主要内容,如果未能解决你的问题,请参考以下文章