我在C(嵌入在C ++中)中有一个简单(但性能至关重要)的算法来操作数据缓冲区......算法“自然地”使用64位大端寄存器值 - 我想优化这使用汇编程序直接访问进位标志和BSWAP,因此避免一次一个字节地操作64位值。
我希望解决方案可以在OS /编译器之间移植 - 最低限度地支持GNU g ++和Visual C ++ - 以及Linux和Windows之间。显然,对于这两个平台,我假设一个支持x86-64指令集的处理器。
我找到了this document about inline assembler for MSVC/Windows,还有几个片段通过Google详细说明了g ++的不兼容语法。我接受我可能需要在每种方言中单独实现此功能。我无法找到足够详细的语法/设施文档来解决这个问题。
我正在寻找的是明确的文档,详细说明了我可用的设施 - 包括MS和GNU工具集。虽然我多年前写了一些32位汇编程序,但我生锈了 - 我会从一个简明的文档中受益,详细说明可以在汇编级别获得设施。
更复杂的是,我想使用Visual C ++ Express Edition 2010编译Windows ...我认识到这是一个32位编译器 - 但是,我想知道,是否可以嵌入64位汇编成可执行文件?我只关心64位性能,我计划手工编写。
任何人都可以提供任何指示(请原谅双关语......)?
答案 0 :(得分:3)
为了让您体验路径中的障碍,这里有一个简单的内联汇编程序功能,有两种方言。首先,Borland C ++ Builder版本(我认为这也是在MSVC ++下编译的):
int BNASM_AddScalar (DWORD* result, DWORD x)
{
int carry = 0 ;
__asm
{
mov ebx,result
xor eax,eax
mov ecx,x
add [ebx],ecx
adc carry,eax // Return the carry flag
}
return carry ;
}
现在,g ++版本:
int BNASM_AddScalar (DWORD* result, DWORD x)
{
int carry = 0 ;
asm volatile (
" addl %%ecx,(%%edx)\n"
" adcl $0,%%eax\n" // Return the carry flag
: "+a"(carry) // Output (and input): carry in eax
: "d"(result), "c"(x) // Input: result in edx and x in ecx
) ;
return carry ;
}
如您所见,差异很大。他们没有办法绕过他们。这些是我为32位环境编写的大型整数算术库。
至于在32位可执行文件中嵌入64位指令,我认为这是禁止的。据我了解,32位可执行文件在32位模式下运行,任何64位指令只会生成陷阱。
答案 1 :(得分:3)
不幸的是,MSVC ++不支持64位代码中的内联汇编,也不支持__emit。使用MSVC ++,您应该在单独的.asm文件中实现代码片段,并使用其余代码编译和链接它们,或者使用如下的脏黑客(针对32位代码实现作为概念证明):
#include <windows.h>
#include <stdio.h>
unsigned char BswapData[] =
{
0x0F, 0xC9, // bswap ecx
0x89, 0xC8, // mov eax, ecx
0xC3 // ret
};
unsigned long (__fastcall *Bswap)(unsigned long) =
(unsigned long (__fastcall *)(unsigned long))BswapData;
int main(void)
{
DWORD dummy;
VirtualProtect(BswapData, sizeof(BswapData), PAGE_EXECUTE_READWRITE, &dummy);
printf("0x%lX\n", Bswap(0x10203040));
return 0;
}
输出:0x40302010
我认为你不仅应该用gcc做同样的事情,而且能用Linux做两件小差异(VirtualProtect()是一个,调用约定是另一个)。
编辑:这是如何在Windows上以64位模式为64位值完成BSWAP(未经测试):
unsigned char BswapData64[] =
{
0x48, 0x0F, 0xC9, // bswap rcx
0x48, 0x89, 0xC8, // mov rax, rcx
0xC3 // ret
};
unsigned long long (*Bswap64)(unsigned long long) =
(unsigned long long (*)(unsigned long long))BswapData64;
其余的都是微不足道的。
答案 2 :(得分:1)
有许多函数可用于交换字节序,例如来自BSD套接字:
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
64位不太便携:
unsigned __int64 _byteswap_uint64(unsigned __int64); // Visual C++
int64_t __builtin_bswap64 (int64_t x). // GCC
每次在标准C ++中无法表达某些内容时,不要求助于汇编。
答案 3 :(得分:0)
根据定义,C或C ++中的 asm 语句不可移植,特别是因为它们与特定指令集相关联。特别是,如果你的汇编语句是针对x86的话,不要指望你的代码在ARM上运行。
此外,即使在64位x86-64(即现代PC-s)的相同硬件平台上,不同的系统(例如Linux与Windows)具有不同的汇编语法和不同的调用约定。所以你应该有几个代码变体。
如果使用GCC,它会为您提供许多可以帮助您的内置功能。并且可能(假设最近的GCC,即4.6版本),它能够非常有效地优化您的功能。
如果性能非常重要,并且您的系统具有GPU(强大的图形卡),您可以考虑在OpenCL或CUDA中重新编码数字内核。
答案 4 :(得分:0)
内联汇编程序不是您的可能性之一:Win64 Visual C编译器不支持__asm
,您需要使用单独的[m | y | n] asm编译文件。