Встроенная функция

Встроенная функция — это функция в программном обеспечении, в теории компиляторов, доступная для использования в данном языке программирования, реализация которой обрабатывается компилятором особым образом. Как правило, компилятор способен заменить исходный вызов функции на последовательность автоматически сгенерированных инструкций, подобно встраиваемой функции[1]. В отличие от встраиваемой функции, компилятор обладает «глубоким знанием» о встроенной функции и, таким образом, может лучше интегрировать и оптимизировать её для конкретной ситуации. Компиляторы, реализующие встроенные функции, могут активировать их только тогда, когда программа запрашивает оптимизацию, в противном случае используя реализацию по умолчанию, предоставляемую системой времени выполнения языка.

Векторизация и распараллеливание

Встроенные функции часто используются для явной реализации векторизации и распараллеливания в языках, которые не поддерживают такие конструкции. Некоторые API, например AltiVec и OpenMP, используют встроенные функции для объявления, соответственно, векторизуемых и поддерживающих многопроцессорную обработку операций на стадии компиляции. Компилятор анализирует встроенные функции и преобразует их в векторные математические или многопроцессорные объектные коды, соответствующие целевой платформе. Некоторые встроенные функции используются для предоставления дополнительных ограничений оптимизатору, например, значения, которые переменная не может принимать[2].

По языкам программирования

C и C++

Компиляторы для языков C и C++ от Microsoft[3], Intel[1] и GCC[4] реализуют встроенные функции, которые напрямую отображаются на инструкции x86 типа SIMD (один поток команд, множество потоков данных) (MMX, SSE, SSE2, SSE3, SSSE3, SSE4, AVX, AVX2, AVX-512, FMA и т. д.). Встроенные функции позволяют отображать код на стандартные ассемблерные инструкции, которые обычно недоступны через C/C++: например, сканирование битов и параллельные операции над 128-битными (SSE) и 256-битными (AVX) данными.

Некоторые компиляторы C и C++ предоставляют непереносимые, платформенно-зависимые встроенные функции. Другие встроенные функции (например, встроенные функции GNU) являются более абстрактными, аппроксимируя возможности нескольких современных платформ, и имеют переносимые резервные реализации (fall back) для платформ без соответствующих инструкций[5]. Для библиотек C++, таких как glm или библиотеки векторной математики от Sony[6], распространена практика достижения переносимости с помощью условной компиляции (на основе специфичных для платформы флагов компилятора). Это позволяет предоставлять полностью переносимые высокоуровневые примитивы (например, тип четырёхэлементного вектора с плавающей запятой), отображённые на соответствующие реализации на низкоуровневых языках, сохраняя при этом преимущества системы типов C++ и встраивания; отсюда и преимущество перед компоновкой с объектными файлами, написанными вручную на ассемблере, с использованием двоичного интерфейса приложений (ABI) языка C.

Примеры

Ниже приведены примеры сигнатур встроенных функций из набора Intel.

 uint64_t __rdtsc        ();                                                          // возвращает внутренний счётчик тактов процессора
 uint64_t __popcnt64     (uint64_t n);                                                // подсчитывает количество установленных битов в n
 uint64_t _umul128       (uint64_t Factor1, uint64_t Factor2, uint64_t* HighProduct); // умножение 64 бит * 64 бит => 128 бит
 __m512   _mm512_add_ps  (__m512 a, __m512 b);                                        // вычисляет a + b для двух векторов из 16 чисел с плавающей запятой
 __m512   _mm512_fmadd_ps(__m512 a, __m512 b, __m512 c);                              // вычисляет a*b + c для трёх векторов из 16 чисел с плавающей запятой

[7]

COBOL

Модуль встроенных функций для COBOL был введён в стандарте ANSI INCITS X3.23a-1989 / ISO 1989:1985/Amd 1:1992.

Java

JIT-компилятор виртуальной машины Java (JVM) HotSpot также имеет встроенные функции для определённых API Java[8]. Встроенные функции HotSpot — это стандартные API Java, которые могут иметь одну или несколько оптимизированных реализаций на некоторых платформах.

PL/I

Стандарт ANSI/ISO для языка PL/I определяет около 90 встроенных функций[9]. Их принято группировать следующим образом[10]:

  • Встроенные функции для обработки строк, такие как INDEX, LENGTH;
  • Арифметические встроенные функции, такие как ABS, CEIL, ROUND;
  • Математические встроенные функции, такие как SIN, COS, LOG, ERF;
  • Встроенные функции для обработки массивов, например ANY, ALL, PROD;
  • Встроенные функции для обработки условий, такие как ONCODE, ONFILE;
  • Встроенные функции управления памятью, например ADDR, POINTER;
  • Встроенные функции ввода-вывода: LINENO;
  • Прочие встроенные функции, такие как DATE и TIME.

Отдельные компиляторы добавляли дополнительные встроенные функции, специфичные для архитектуры машины или операционной системы.

Встроенная функция идентифицируется, если её имя не объявляется и используется по умолчанию, или если она объявляется с атрибутом BUILTIN. Предоставленная пользователем функция с тем же именем может быть подставлена, если объявить её как ENTRY.

Примечания

Ссылки