Visual C ++ x64 добавить с переносом - PullRequest
7 голосов
/ 05 февраля 2012

Поскольку для ADC, похоже, нет ничего встроенного, и я не могу использовать встроенный ассемблер для архитектуры x64 с Visual C ++, что мне следует делать, если я хочу написать функцию с использованием add с переносом, но включить ее вПространство имен C ++?

(Эмуляция с операторами сравнения не возможна. Это 256-мегабитное добавление критично для производительности.)

Ответы [ 3 ]

7 голосов
/ 11 февраля 2012

VS2010 имеет встроенную поддержку компиляции и компоновки кода, написанного на ассемблере и переведенного MASM (ml64.exe). Вам просто нужно прыгнуть через несколько обручей, чтобы включить его:

  • Щелкните правой кнопкой мыши проект в окне обозревателя решений, выберите «Настройка сборки» и установите флажок «masm».
  • Project + Add New Item, выберите шаблон файла C ++, но назовите его thing.asm
  • Убедитесь, что у вас есть целевая платформа x64 для проекта. Build + Configuration Manager, выберите «x64» в поле «Платформа активного решения». Если отсутствует, выберите <New> и выберите x64 из первого комбо. В случае отсутствия вам придется перезапустить программу установки и добавить поддержку для 64-битных компиляторов.

Записать код сборки с использованием синтаксиса MASM, ссылка здесь . Краткое руководство здесь .

Скелет для ассемблерного кода выглядит так:

.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.

6 голосов
/ 24 марта 2015

В MSVC теперь есть значение для ADC: _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.

1 голос
/ 14 февраля 2012

Я реализовал 256-битное целое число, используя массив unsigned long long, и использовал сборку x64 для реализации сложения с переносом.Вот вызывающая программа 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-битных чисел:

#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;
}
...