x64 visual microsoft c++ visual-c++ 64bit inline-assembly intrinsics

x64 - microsoft visual c++ 2017



Visual C++ x64 agregar con llevar (3)

Dado que no parece haber un intrínseco para ADC y no puedo usar el ensamblador en línea para la arquitectura x64 con Visual C ++, ¿qué debo hacer si quiero escribir una función usando agregar con acarreo pero incluirlo en un espacio de nombres de C ++?

(Emular con operadores de comparación no es una opción. Este complemento de 256 megabits es crítico para el rendimiento).


VS2010 tiene soporte integrado para compilar y vincular código escrito en ensamblado y traducido por MASM (ml64.exe). Solo tienes que saltar unos aros para habilitarlo:

  • Haga clic con el botón derecho en el proyecto en la ventana del Explorador de soluciones, Personalizaciones de compilación, marque "masm".
  • Proyecto + Agregar nuevo elemento, elija la plantilla del archivo C ++ pero nómbrelo something.asm
  • Asegúrese de tener el objetivo de la plataforma x64 para el proyecto. Build + Configuration Manager, seleccione "x64" en el combo "Active solution platform". Si falta, seleccione <New> y elija x64 del primer combo. Si falta, tendrá que volver a ejecutar la instalación y agregar soporte para compiladores de 64 bits.

Escriba el código de ensamblaje usando la sintaxis de MASM, la referencia está aquí . El tutorial de inicio rápido está aquí .

El esqueleto para el código de ensamblado se ve así:

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

Y llamado desde código C ++ como este:

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

El soporte de depuración completa está disponible, por lo general querrá al menos usar la ventana Depuración + Registros de Windows +.


Implementé un número entero de 256 bits usando una matriz de ensamblaje x64 unsigned long long usar y usado para implementar el complemento con acarreo. Aquí está la persona que llama 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; }

El add256 se implementa en el ensamblaje:

; 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

Sé que está indicando que no quería un emulado de agregar con la solución de transporte, y quería una solución de alto rendimiento, pero, aún así, puede considerar la siguiente solución de C ++ que tiene una buena forma de simular números de 256 bits:

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


Ahora hay un intrínseco para ADC en _addcarry_u64 : _addcarry_u64 . El siguiente código

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

produce la siguiente salida de ensamblaje de 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]

que tiene un add y tres adc como se esperaba.

Editar:

Parece haber cierta confusión sobre lo que hace _addcarry_u64 . Si mira la documentación de Microsoft para esto a la que me he vinculado al inicio de esta respuesta, muestra que no requiere ningún hardware especial. Esto produce adc y funcionará en todos los procesadores x86-64 (y _addcarry_u32 funcionaría en procesadores aún más antiguos). Funciona bien en el sistema Ivy Bridge en el que lo probé.

Sin embargo, _addcarryx_u64 requiere adx (como se muestra en la documentación de MSFT) y, de hecho, no se ejecuta en mi Ivy Bridge System.