2012-02-04 1 views
6

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

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

+0

Сообщите нам об этом «256 megabit add». Вполне вероятно, что выполнение нескольких добавлений одновременно с использованием SIMD будет значительно быстрее, даже если учитывать, что перенос должен обрабатываться как дополнительный шаг. –

+0

Я уже сделал это немного исследований. См. Http://stackoverflow.com/questions/8866973/can-long-integer-routines-benefit-from-sse. – jnm2

+1

@ jnm2 - образ x64, кажется, пишет отдельный код сборки и вызывает это из вашей функции C++. Ассемблер уже является частью пакета. –

ответ

4

There is now an instrinsic для ADC в MSVC: _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, как ожидалось.

Edit:

Там, кажется, некоторая путаница относительно того, что _addcarry_u64 делает. Если вы посмотрите на документацию Microsoft по этому вопросу, с которой я связался в начале этого ответа, это показывает, что он не требует специального оборудования. Это производит adc, и он будет работать на всех процессорах x86-64 (и _addcarry_u32 будет работать на более старых процессорах). Он отлично работает в системе Ivy Bridge, в которой я тестировал ее.

Однако _addcarryx_u64 требует adx (как показано в документации MSFT), и действительно, он не работает на моей системе моста Ivy.

+1

Этот ответ требует отказа от ответственности, эту инструкцию можно использовать только для процессоров Core 4-го поколения (Haswell и выше). Еще 5-10 лет и номер телефона поддержки, прежде чем вы сможете слепо зависеть от его доступности. –

+0

@HansPassant Я не могу это подтвердить. У вас есть ссылка на это? – jnm2

+0

https://software.intel.com/en-us/node/523867 –

7

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

  • Щелкните правой кнопкой мыши проект в окне обозревателя решений, выполните настройки, отметьте «masm».
  • Project + Добавить новый элемент, выбрать шаблон C++ файла, но имя его something.asm
  • Убедитесь, что у вас есть целевая платформа x64 для проекта. Build + Configuration Manager, выберите «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.

+0

Идеальное решение в этом случае было бы встроенными функциями (встроенная сборка). Использование ассемблера и связывание в объектных файлах не сделают этого, а 64-разрядный код в MSVC не разрешит встроенную сборку. Таким образом, это означает, что OP должен написать много других функций (которые компилятор, вероятно, делает хорошую работу уже) в сборке, а также избежать вызовов функций. –

1

Я реализовал 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; 
} 
+0

Извините, что опоздал, но решение C++ неверно. В качестве упрощения рассмотрим a = 01 и b = 11 с carry = 1, тогда c = 01 с carry = 1, но c knivil