嵌入汇编程序来处理便携式C ++中的64位寄存器

时间:2011-10-22 12:38:09

标签: c++ assembly g++ x86-64 visual-c++-2010

我在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位性能,我计划手工编写。

任何人都可以提供任何指示(请原谅双关语......)?

5 个答案:

答案 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编译文件。

相关问题