Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visual C++ x64 add with carry

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

like image 888
jnm2 Avatar asked Feb 04 '12 23:02

jnm2


People also ask

How do I add x64 platform to Visual Studio?

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.

How do I open x64 native tools in CMD?

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.

How do I install Microsoft Visual C++ MSVC compiler toolset?

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.

How do I change my Win32 win64 code to vs64?

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.


2 Answers

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.

like image 159
Z boson Avatar answered Oct 09 '22 17:10

Z boson


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:

  • Right-click the project in the Solution Explorer window, Build Customizations, tick "masm".
  • Project + Add New Item, pick the C++ File template but name it something.asm
  • Ensure you've got the x64 platform target for the project. Build + Configuration Manager, select "x64" in the "Active solution platform" combo. If missing, select <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.

like image 31
Hans Passant Avatar answered Oct 09 '22 15:10

Hans Passant