Получить GCC для использования логики Carry для произвольной точной арифметики без встроенной сборки?

При работе с арифметикой произвольной точности (например, 512-битными целыми числами) существует ли способ заставить GCC использовать ADC и подобные инструкции без использования встроенной сборки?

Первый взгляд на исходный код GMP показывает, что они просто имеют сборки для каждой поддерживаемой платформы.

Вот тестовый код, который я написал, который добавляет два 128-битных номера из командной строки и печатает результат. (Вдохновленный мини-gmp add_n):

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main (int argc, char **argv)
{
    uint32_t a[4];
    uint32_t b[4];
    uint32_t c[4];
    uint32_t carry = 0;

    for (int i = 0; i < 4; ++i)
    {
        a[i] = strtoul (argv[i+1], NULL, 16);
        b[i] = strtoul (argv[i+5], NULL, 16);
    }

    for (int i = 0; i < 4; ++i)
    {
        uint32_t aa = a[i];
        uint32_t bb = b[i];
        uint32_t r = aa + carry;
        carry = (r < carry);
        r += bb;
        carry += (r < bb);
        c[i] = r;
    }

    printf ("%08X%08X%08X%08X + %08X%08X%08X%08X =\n", a[3], a[2], a[1], a[0], b[3], b[2], b[1], b[0]);
    printf ("%08X%08X%08X%08X\n", c[3], c[2], c[1], c[0]);

    return 0;
}

GCC -O3 -std=c99 Не выполняет никаких инструкций adc, как отмечено objdump. Моя версия gcc i686-pc-mingw32-gcc (GCC) 4.5.2.

Ответы

Ответ 1

GCC будет использовать флаг переноса, если он увидит, что ему нужно:
Например, при добавлении двух значений uint64_t на 32-разрядную машину это должно привести к одному 32-разрядному ADD плюс одному 32-разрядному ADC. Но помимо тех случаев, когда компилятор вынужден использовать перенос, его, вероятно, нельзя убедить сделать это без ассемблера. Следовательно, может быть полезно использовать самый большой целочисленный тип, доступный, чтобы GCC мог оптимизировать операции, эффективно сообщив ему, что единичные "компоненты" значения принадлежат вместе.

Для простого добавления другим способом вычисления переноса может быть просмотр соответствующих битов в операндах, например:

uint32_t aa,bb,rr;
bool msbA, msbB, msbR, carry;
// ...

rr = aa+bb;

msbA = aa >= (1<<31); // equivalent: (aa & (1<<31)) != 0;
msbB = bb >= (1<<31);
msbR = rr >= (1<<31);


carry = (msbA && msbB) || ( !msbR && ( msbA || msbB) );