_addcarry_u64 и _addcarryx_u64 с MSVC и ICC

MSVC и ICC поддерживают встроенные _addcarry_u64 и _addcarryx_u64.

В соответствии с Intel Intrinsic Guide и белая бумага должен отображаться на adcx и adox соответственно. Однако, посмотрев на сгенерированную сборку, ясно, что они сопоставляются с adc и adcx соответственно, и нет никакого внутреннего, который отображается на adox.

Кроме того, сообщая компилятору о включении AVX2 с /arch:AVX2 в MSVC или -march=core-avx2 с ICC на Linux, не имеет значения.  Я не уверен, как включить ADX с MSVC и ICC.

Документация для MSVC содержит список _addcarryx_u64 с технологией ADX, тогда как _addcarry_u64 не имеет перечисленных технологий. Тем не менее, ссылка в документации MSVC для этих встроенных функций напрямую связана с руководством Intel Intrinsic, которое противоречит собственно документации MSVC и сгенерированной сборке.

Из этого я пришел к выводу, что Intel Intrinsic guide и белая бумага ошибочны.

Это означает, что MSVC считает, что он не позволяет встроенную сборку, он должен предоставить способ использования adc, который он выполняет с помощью _addcarry_u64.

Одним из больших преимуществ adcx и adox является то, что они работают с разными флагами (переносят CF и overflow OF), что позволяет использовать две независимые параллельные цепи переноса. Однако, поскольку для adox нет неотъемлемой возможности, как это возможно? С ICC по крайней мере можно использовать встроенную сборку, но это невозможно с MSVC в 64-битном режиме.


Документация Microsoft и Intel (как белая бумага, так и встроенный гид онлайн) соглашаются теперь.

Внутренняя документация _addcarry_u64 говорит только о adc. Внутренний _addcarryx_u64 может создавать либо adcx, либо adox. Однако с MSVC 2013 и 2015, _addcarryx_u64 создает adcx. ICC производит оба.

Ответ 1

Они отображаются на adc, adcx AND adox. Компилятор решает, какие инструкции использовать, в зависимости от того, как вы их используете. Если вы выполняете два добавления большого числа параллельно, компилятор будет использовать adcx и adox для большей пропускной способности. Например:

unsigned char c1 = 0, c2 = 0
for(i=0; i< 100; i++){ 
    c1 = _addcarry_u64(c1, res[i], a[i], &res[i]);
    c2 = _addcarry_u64(c2, res[i], b[i], &res[i]);
}

Ответ 2

Соответственно, GCC не поддерживает ADOX и ADCX на данный момент. "В настоящий момент" включает GCC 6.4 (Fedora 25) и GCC 7.1 (Fedora 26). GCC эффективно отключает встроенные функции, но он все еще рекламирует поддержку, определяя __ADX__ в препроцессоре. Также см. Проблема 67317, создание глупого кода для _addcarry_u32/_addcarry_u64. Большое спасибо Xi Ruoyao за обнаружение проблемы.

Согласно Uros Bizjak в списке рассылки справки GCC, GCC может никогда не поддерживать внутренние функции. Также см. GCC не генерирует ADCX или ADOX для _addcarryx_u64.

У Clang есть свой набор проблем в отношении ADOX и ADCX. Clang 3.9 и 4.0 при попытке их использования. Также см. Проблема 34249, Паника при использовании _addcarryx_u64 с Clang 3.9. Согласно Крейгу Топперу, это должно быть исправлено в Clang 5.0.

Приношу свои извинения за отправку информации по запросу MSVC. Это один из немногих ударов при поиске информации об использовании встроенных функций.