Кто такой vfmaddrnd231pd?

vfmaddrnd231pd — это уникальная инструкция из набора FMA. Большинство инструкций этого набора имеют мнемоники вроде vfmadd231pd, но здесь rnd означает нечто совершенно специфичное. В справочнике Intel Software Development Manual она описана как:

VFMADDRND231PD – Fused Multiply-Add of Packed Double-Precision Floating-Point Values with rounding control

[…] Байт-операнд определяет несколько битовых полей, которые управляют режимом округления, DAZ, FTZ и подавлением исключений (см. таблицу 5-3).

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

Вот только эта инструкция не существует. Ассемблер GNU не распознаёт её. Встроенные функции Intel C/C++ _mm_fmaddround_pd и _mm256_fmaddround_pd, которые упоминаются в SDM, отсутствуют не только в Clang, но и в самом ICC. Попытка собрать инструкции вручную из списка опкодов SDM выявляет другую проблему:

VEX.DDS.128.66.0F3A.W1 B8 /r /ib

VEX — это VEX-префикс, но что такое DDS? Этот опкод — единственное упоминание DDS во всём SDM, и этот ответ на Stack Overflow говорит, что DDS используется только в документационных целях и был удалён из более поздних версий SDM, но то, что он всё ещё здесь присутствует, вызывает подозрения. И когда попытаться выполнить инструкцию на процессоре, поддерживающем набор FMA, например на моём Haswell, она выдаст #UD («UnDefined instruction» — неопределённая инструкция)!

Странным казалось уже то, что SDM содержит только vfmaddrnd231pd, а не другие варианты с pd/ps/sd/ss или 132/213/231, которые есть у других инструкций FMA, но это окончательно подтвердило подозрения. Тогда же выяснилось, что в издании SDM 2026 года эта инструкция отсутствует, хотя была в версии 2024 года.

Честно говоря, я в растерянности. Эта инструкция не похожа на опечатку в документации, так как в справочнике есть уникальные таблицы и псевдокод, не похожие ни на какую другую инструкцию. Моя лучшая гипотеза — что Intel хотела добавить набор vfmaddrnd*, но в последний момент обнаружила проблему и удалила его с процессора, но не совсем правильно обновила SDM. Но я не могу это подтвердить.

Если кто-нибудь из читателей знает кого-то из Intel, я буду благодарен за ответ.

Дополнение

seastarer и 039234562456 дали мне достаточно подсказок, чтобы разобраться в этом.

В 2023 году Intel не смогла добавить поддержку AVX-512 в потребительские процессоры из-за аппаратных ограничений, но всё ещё хотела добавить кое-какие фишки из AVX-512. Так родилось AVX10 — новое расширение, похожее примерно на AVX-512, но с условной поддержкой длины векторов 512 бит.

AVX-512 использует необычную схему кодирования инструкций с префиксом EVEX. Для компактности биты, ответственные за статический режим округления, переиспользуются для ширины операции (128, 256 или 512 бит), поэтому только скалярные и 512-битные операции могут иметь кастомизируемое округление.

Для AVX10 это была проблема, так как оно не поддерживает 512-битные операции: если интерпретировать ту же инструкцию как операцию над 256-битными значениями вместо 512-битных, разные процессоры вели бы себя по-разному. Поэтому AVX10 должно было использовать другую схему кодирования для своих собственных режимов округления.

Моя теория такова: изначально Intel решила, что самый простой способ — это добавить байт-операнд, который управляет округлением, вот так и появились vfmaddrnd231pd и подобные инструкции для других арифметических операций. Обратите внимание, что она использует префикс VEX, а не специфичный для AVX-512 префикс EVEX — это позволило бы процессорам без 512-битных векторов избежать обработки префикса EVEX вообще. Поскольку добавлен целый байт, его использовали полностью: 2 бита для режима округления, 6 бит для всего остального, что казалось полезным.

Похоже, к 2024 году Intel решила, что такой подход не сработает. И это понимаю: странно, что vfmaddrnd231pd была мощнее, чем инструкции AVX-512, но поддерживала только 256-битные векторы. А если бы она поддерживала 512-битные векторы, встроенное в AVX-512 управление режимом округления во многом стало бы устаревшим.

Так Intel пошла на жертву, выделила последний неиспользуемый бит в префиксе EVEX для включения режимов округления для 256-битных векторов и переиспользовала кодирование инструкций AVX-512 для AVX10. Это также позволило переиспользовать синтаксис сборки AVX-512 и избежать введения новых мнемоник, и тогда мнемоника vfmaddrnd231pd и других стали ненужными. Во время этого процесса кто-то забыл удалить vfmaddrnd231pd из черновика SDM.

К 2025 году AVX-512 стал стандартом де-факто из-за популярности Zen 4, и Intel наконец решила сделать поддержку 512-битных операций безусловной в AVX10. Это сделало весь этот костыль ненужным, поэтому Intel обновила техническое описание AVX10:

Удалены ссылки на максимальный размер векторного регистра 256 бит, перечисление поддержки длины вектора и 256-битные инструкции с поддержкой встроенного округления.

…и отправила патчи в GCC для удаления поддержки 256-битного округления:

В этом новом техническом описании все платформы будут поддерживать ширину вектора 512 бит (раньше E-core поддерживал до 256 бит, приводя к гибридным клиентам и Atom Server с 256 битами). Также 256-битное округление не так полезно, потому что на E-core теперь есть функция округления напрямую и нет нужды использовать 256-битное округление как какой-то костыль. Оборудование удалит эту поддержку.

Эту теорию можно частично подтвердить, посмотрев на патчи. Один из них называется:

Revert "AVX10.2 ymm rounding: Support vfmadd{132, 231, 213}p{s, d, h} intrins"

…и удаляет множество встроенных функций, включая _mm256_fmadd_round_pd, которая названа очень похоже на документированную встроенную функцию для фантомной инструкции, _mm256_fmaddround_pd.

Позже кто-то в Intel понял, что vfmaddrnd231pd всё ещё упоминается в SDM, и удалил это упоминание.

Подводя итог: я верю, что vfmaddrnd231pd имеет происхождение из AVX10. Думаю, она была добавлена на этапе прототипирования AVX10, затем переработана для использования другой схемы кодирования, пока наконец не стала ненужной. Рыночные давления, сроки и странный технический долг настолько сильно повлияли на процесс проектирования, что пришлось переделывать вещи на лету, и на определённом этапе кто-то допустил ошибку и случайно документировал инструкцию, которая никогда не существовала в выпущенном оборудовании.