Since there doesn't seem to be an intrinsic for ADC and I can't use inline assembler for x64 architecture with Visual C++, what should I do if I want to write a function using add with carry but include it in a C++ namespace?
(Emulating with comparison operators is not an option. This 256 megabit add is performance critical.)
From the BUILD menu in Visual Studio, select Configuration Manager. From the Active solution platform drop-down list, select New. The New Solution Platform dialog displays. In the Type or select new platform combination box, select x64.
To access these command prompts on Windows, on the Start menu, open the folder for your version of Visual Studio, and then choose one of the x64 native or cross-tool developer command prompts.
Download and install the tools When you run the downloaded executable, it updates and runs the Visual Studio Installer. To install only the tools you need for C++ development, select the Desktop development with C++ workload. You can select optional libraries and toolsets to include under Installation details.
Pull down the drop-down under “Active solution platform” which currently displays “Win32” and select New. In the drop-down for “Type or select the new platform”, select “x64”. If x64 does not exist, you will need to install the Visual Studio 2008 64-bit extensions, as shown in step 1. Click OK.
There is now an instrinsic for ADC
in MSVC: _addcarry_u64
. The following code
#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);
}
produces the following assembly output from 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]
which has one add
and three adc
as expected.
Edit:
There seems to be some confusion as to what _addcarry_u64
does. If you look at Microsoft's documentation for this which I linked to at the start of this answer it shows that it does not require any special hardware. This produces adc
and it will work on all x86-64 processors (and _addcarry_u32
would work on even older processors). It works fine on the Ivy Bridge system I tested it on.
However, _addcarryx_u64
does require adx
(as shown in MSFT's documentation) and indeed it fails to run on my Ivy Bridge System.
VS2010 has built-in support for compiling and linking code written in assembly and translated by MASM (ml64.exe). You just have to jump through a few hoops to enable it:
<New>
and pick x64 from the first combo. If missing you'll have to re-run setup and add support for 64-bit compilers.Write assembly code using MASM syntax, reference is here. Quick start tutorial is here.
The skeleton for the assembly code looks like this:
.CODE
PUBLIC Foo
Foo PROC
ret ; TODO: make useful
Foo ENDP
END
And called from C++ code like this:
extern "C" void Foo();
int main(int argc, char* argv[])
{
Foo();
return 0;
}
Full debugging support is available, you'll typically want to at least use the Debug + Windows + Registers window.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With