Visual C ++ x64附带进位

时间:2012-02-04 23:42:38

标签: c++ visual-c++ 64-bit inline-assembly intrinsics

由于似乎没有ADC的固有内容,我不能使用Visual C ++的x64架构使用内联汇编程序,如果我想使用add with carry编写函数但是将其包含在一个函数中,我该怎么办? C ++名称空间?

(使用比较运算符进行仿真不是一种选择。这256兆位的添加对性能至关重要。)

3 个答案:

答案 0 :(得分:7)

VS2010内置支持编译和链接汇编编写并由MASM(ml64.exe)翻译的代码。你只需要跳过几个环来启用它:

  • 在解决方案资源管理器窗口中右键单击项目,构建自定义,勾选“masm”。
  • 项目+添加新项目,选择C ++文件模板,但将其命名为 something.asm
  • 确保您已获得该项目的x64平台目标。 Build + Configuration Manager,在“Active solution platform”组合中选择“x64”。如果缺少,请选择<New>并从第一个组合中选择x64。如果遗漏,您将不得不重新运行安装程序并添加对64位编译器的支持。

使用MASM语法编写汇编代码,引用is here。快速入门教程is here

汇编代码的框架如下所示:

.CODE
PUBLIC Foo
Foo PROC
  ret                    ; TODO: make useful
Foo ENDP
END

从这样的C ++代码调用:

extern "C" void Foo();

int main(int argc, char* argv[])
{
    Foo();
    return 0;
}

完全调试支持可用,您通常希望至少使用Debug + Windows + Registers窗口。

答案 1 :(得分:6)

MSVC中ADC的{​​p> There is now an instrinsic_addcarry_u64。以下代码

#include <inttypes.h>
#include <intrin.h>
#include <stdio.h>

typedef struct {
    uint64_t x1;
    uint64_t x2;
    uint64_t x3;
    uint64_t x4;
} uint256;

void add256(uint256 *x, uint256 *y) {
    unsigned char c = 0;
    c = _addcarry_u64(c, x->x1, y->x1, &x->x1);
    c = _addcarry_u64(c, x->x2, y->x2, &x->x2);
    c = _addcarry_u64(c, x->x3, y->x3, &x->x3);
    _addcarry_u64(c, x->x4, y->x4, &x->x4);
}

int main() {
    //uint64_t x1, x2, x3, x4;
    //uint64_t y1, y2, y3, y4;
    uint256 x, y;
    x.x1 = x.x2 = x.x3 = -1; x.x4 = 0;
    y.x1 = 2; y.x2 = y.x3 = y.x4 = 0;

    printf(" %016" PRIx64 "%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "\n", x.x4, x.x3, x.x2, x.x1);
    printf("+");
    printf("%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "\n", y.x4, y.x3, y.x2, y.x1);
    add256(&x, &y);
    printf("=");
    printf("%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "\n", x.x4, x.x3, x.x2, x.x1);
}

从Visual Studio Express 2013生成以下程序集输出

mov rdx, QWORD PTR x$[rsp]
mov r8, QWORD PTR x$[rsp+8] 
mov r9, QWORD PTR x$[rsp+16]
mov rax, QWORD PTR x$[rsp+24]
add rdx, QWORD PTR y$[rsp]
adc r8, QWORD PTR y$[rsp+8]
adc r9, QWORD PTR y$[rsp+16]
adc rax, QWORD PTR y$[rsp+24]

预期有一个add和三个adc

编辑:

_addcarry_u64的作用似乎有些混乱。如果您查看Microsoft在此答案开头时链接到的文档,则表明它不需要任何特殊硬件。这会生成adc,它将适用于所有x86-64处理器(_addcarry_u32甚至可以在较旧的处理器上运行)。它在我测试过的Ivy Bridge系统上工作正常。

但是,_addcarryx_u64确实需要adx(如MSFT文档中所示),实际上它无法在我的Ivy Bridge系统上运行。

答案 2 :(得分:1)

我使用unsigned long long数组实现了256位整数,并使用x64汇编来实现带进位的add。这是C ++调用者:

#include "stdafx.h"

extern "C" void add256(unsigned long long *a, unsigned long long * b, unsigned long long *c);

int _tmain(int argc, _TCHAR* argv[])
{
    unsigned long long a[4] = {0x8000000000000001, 2, 3, 4};
    unsigned long long b[4] = {0x8000000000000005, 6, 7, 8};
    unsigned long long c[4] = {0, 0, 0, 0};
    add256(a, b, c); // c[] == {6, 9, 10, 12};
    return 0;
}

add256在汇编中实现:

    ; void add256(unsigned long long *a, unsigned long long * b, unsigned long long *c)

.CODE
PUBLIC add256
add256 PROC

    mov                 qword ptr [rsp+18h],r8    
    mov                 qword ptr [rsp+10h],rdx    
    mov                 qword ptr [rsp+8],rcx    
    push                rdi    

    ; c[0] = a[0] + b[0];

    mov                 rax,qword ptr 16[rsp]
    mov                 rax,qword ptr [rax]    
    mov                 rcx,qword ptr 24[rsp]
    add                 rax,qword ptr [rcx]    
    mov                 rcx,qword ptr 32[rsp]
    mov                 qword ptr [rcx],rax    

    ; c[1] = a[1] + b[1] + CARRY;

    mov                 rax,qword ptr 16[rsp]
    mov                 rax,qword ptr [rax+8]    
    mov                 rcx,qword ptr 24[rsp]
    adc                 rax,qword ptr [rcx+8]    
    mov                 rcx,qword ptr 32[rsp]
    mov                 qword ptr [rcx+8],rax    

    ; c[2] = a[2] + b[2] + CARRY;

    mov                 rax,qword ptr 16[rsp]
    mov                 rax,qword ptr [rax+10h]    
    mov                 rcx,qword ptr 24[rsp]
    adc                 rax,qword ptr [rcx+10h]    
    mov                 rcx,qword ptr 32[rsp]
    mov                 qword ptr [rcx+10h],rax    

    ; c[3] = a[3] + b[3] + CARRY;

    mov                 rax,qword ptr 16[rsp]
    mov                 rax,qword ptr [rax+18h]    
    mov                 rcx,qword ptr 24[rsp]
    adc                 rax,qword ptr [rcx+18h]    
    mov                 rcx,qword ptr 32[rsp]
    mov                 qword ptr [rcx+18h],rax    

    ; }

    pop                 rdi    
    ret    

    add256              endp

    end                        

我知道您表示您不希望使用进位解决方案进行模拟添加,并且需要高性能解决方案,但是,您仍然可以考虑以下仅使用C ++的解决方案,它具有模拟256位数的好方法:< / p>

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    unsigned long long a[4] = {0x8000000000000001, 2, 3, 4};
    unsigned long long b[4] = {0x8000000000000005, 6, 7, 8};
    unsigned long long c[4] = {0, 0, 0, 0};
    c[0] = a[0] + b[0]; // 6
    c[1] = a[1] + b[1] + (c[0] < a[0]); // 9
    c[2] = a[2] + b[2] + (c[1] < a[1]); // 10
    c[3] = a[3] + b[3] + (c[2] < a[2]); // 12
    return 0;
}