Standard intrinsic functions

Сергей Тиунов
Сергей Тиунов

Мотивация

В целочисленных приложениях (обработка видео, звука и других числовых данных) периодически возникают ситуации, когда удобная инструкция есть в целевом процессоре, но является нестандартной для языка. Современные компиляторы имеют для таких ситуаций встроенные intrinsic-функции, однако во всех компиляторах они называются по-разному, и иногда даже имеют разную семантику.

Пример

Например, если нужно определить двоичный логарифм числа, то на x86 есть инструкции BSR (давно) и LZCNT (недавно), а на ARM есть инструкция CLZ (ARMv5+). Соответствующие intrinsic-функции в разных компиляторах:

 

unsigned char _BitScanReverse(unsigned long * Index, unsigned long Mask); // MSVC x86 and ARM, Intel C Compiler
unsigned int _arm_clz(unsigned int _Rm); // MSVC ARM
unsigned int __lzcnt(unsigned int); // MSVC x86
int __builtin_clz (unsigned int x); // GCC and clang - on any supported processor (?)
int __builtin_ffs (unsigned int x); // GCC and clang - on any supported processor (?)
unsigned int __clz(uint32_t x); // ARM C Language Extensions (ACLE) - supported by GCC (clang?)

Эти функции имеют не только разные сигнатуры, но и разную семантику:

  • BSR - Bit Scan Reverse - это индекс старшего значащего бита (если операнд 0, то результат не определен)
  • FFS - Find First Set: FFS(x) = x ? 1 + BSR(x) : 0
  • CLZ - Count Leading Zeros - это количество старших нулей: CLZ(x) = 8*sizeof(x) - FFS(x)

В результате приходится писать собственные функции-обертки с портянкой #ifdef:

inline int32_t bit_scan_reverse(int32_t x) {
#if defined(_MSC_VER)
    unsigned long result;
    BitScanReverse(&result, x);
    return result;
#elif defined(__GNUC__) || defined(__clang__)
    return 31 - __builtin_clz(x);
#endif
}

Также известен подводный камень на x86: функция __lzcnt честно компилируется MSVC в инструкцию LZCNT, но если она не поддерживается вашим Intel'ом (а поддерживается она сравнительно недавно), то он вместо CLZ сделает BSR и получит совершенно противоположный результат.

Предложение

Предлагается включить в стандартную библиотеку следующие распространенные операции с фиксированным интерфейсом и хорошо определенным поведением:

  • bit scan reverse/forward
  • bit rotate left/right
  • byte swap
  • pop count (количество установленных бит)
  • parallel bit extract/deposit (получение/установка бит, заданных маской)
  • ??? (ваши предложения)

... с соответствующими перегрузками для доступных целочисленных типов.

Такие инструкции как BSR или PDEP/PEXT сложно заменить алгоритмически - если они требуются в вычислениях нужен двоичный логарифм, то скорее всего его придется искать. С другой стороны эти инструкции не настолько просты, чтобы компилятор мог распознать и заменить их. Поэтому стандартная библиотека - это хорошее место для этих функций.

Возможно, будучи в стандартной библиотеке, эти функции не только помогут решать вышеприведенные задачи, но и будут способствовать появлению новых эффективных алгоритмов и реализаций известных функций. Например, задача преобразования числа в строку может быть решена более эффективно, если известен порядок числа, т.е. количество десятичных знаков.

Ссылки

MSVC Compiler intrinsics

GCC built-in functions

x86 BMI instructions

Intel Intrinsics Guide

ARM C Language Extensions

 

16
рейтинг
3 комментария
nenomius
nenomius
h4tred
nenomius, не всё определяется только играми с битами. Есть ещё SIMD. К примеру.
h4tred
h4tred
Плюс всякие SIMD.
h4tred
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).