stringtranslate.com

Встроенный ассемблер

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

Мотивация и альтернативы

Внедрение кода на языке ассемблера обычно выполняется по одной из следующих причин: [1]

С другой стороны, встроенный ассемблер создает прямую проблему для самого компилятора, поскольку он усложняет анализ того, что делается с каждой переменной, ключевой частью распределения регистров. [2] Это означает, что производительность может фактически снизиться. Встроенный ассемблер также усложняет будущее портирование и обслуживание программы. [1]

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

Синтаксис

В языковых стандартах

Стандарт ISO C++ и стандарты ISO C (приложение J) определяют условно поддерживаемый синтаксис для встроенного ассемблера:

Объявление asm имеет форму
  asm-declaration :
     asm ( string-literal );
Объявление asm поддерживается условно; его значение определяется реализацией. [3]

Однако это определение редко используется в реальном языке C, поскольку оно одновременно слишком либерально (в интерпретации) и слишком ограничено (в использовании только одного строкового литерала).

В реальных компиляторах

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

В целом, компиляторы C/C++ поддерживают два типа встроенного ассемблера:

Два семейства расширений представляют различные понимания разделения труда при обработке встроенного ассемблера. Форма GCC сохраняет общий синтаксис языка и разделяет то, что должен знать компилятор: что необходимо и что изменено. Она явно не требует от компилятора понимания имен инструкций, поскольку компилятору нужно только заменить свои назначения регистров, плюс несколько операций mov , чтобы обработать входные требования. Однако пользователь склонен неправильно указывать затираемые регистры. Форма MSVC встроенного доменно-специфического языка обеспечивает простоту написания, но она требует от самого компилятора знания имен опкодов и их свойств затирания, что требует дополнительного внимания при обслуживании и портировании. [7] Все еще возможно проверить ассемблер в стиле GCC на наличие ошибок затирания, зная набор инструкций. [8]

GNAT (интерфейс языка Ada для пакета GCC) и LLVM используют синтаксис GCC. [9] [10] Язык программирования D использует DSL, аналогичный расширению MSVC, официально предназначенному для x86_64, [11] но LDC на основе LLVM также предоставляет синтаксис в стиле GCC для каждой архитектуры. [12] MSVC поддерживает только встроенный ассемблер на 32-битной платформе x86. [5]

С тех пор язык Rust перешел на синтаксис, абстрагирующий встроенные опции сборки дальше, чем версия LLVM (стиль GCC). Он предоставляет достаточно информации, чтобы преобразовать блок во внешне собранную функцию, если бэкенд не может справиться со встроенной сборкой. [7]

Примеры

Системный вызов в GCC

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

Следующий пример кода C показывает оболочку системного вызова x86 в синтаксисе ассемблера AT&T , использующую GNU Assembler . Такие вызовы обычно пишутся с помощью макросов; полный код включен для ясности. В этом конкретном случае оболочка выполняет системный вызов числа, указанного вызывающим, с тремя операндами, возвращая результат. [13]

Подводя итог, GCC поддерживает как базовый , так и расширенный ассемблер. Первый просто передает текст дословно ассемблеру, а последний выполняет некоторые замены для местоположений регистров. [4]

extern int errno ;  int syscall3 ( int num , int arg1 , int arg2 , int arg3 ) { int res ; __asm__ ( "int $0x80" /* выполнить запрос к ОС */ : "=a" ( res ), /* вернуть результат в eax ("a") */ "+b" ( arg1 ), /* передать arg1 в ebx ("b") [как вывод "+", поскольку системный вызов может его изменить] */ "+c" ( arg2 ), /* передать arg2 в ecx ("c") [то же самое] */ "+d" ( arg3 ) /* передать arg3 в edx ("d") [то же самое] */ : "a" ( num ) /* передать номер системного вызова в eax ("a") */ : "memory" , "cc" , /* сообщить компилятору, что коды памяти и условий были изменены */ "esi" , "edi" , "ebp" ); /* эти регистры также затираются [изменяются системным вызовом] */                                        /* Операционная система вернет отрицательное значение в случае ошибки;  * оболочки возвращают -1 в случае ошибки и устанавливают глобальную переменную errno */ if ( -125 <= res && res < 0 ) { errno = - res ; res = -1 ; } return res ; }                  

Специфическая для процессора инструкция на языке D

В этом примере встроенного ассемблера из языка программирования D показан код, который вычисляет тангенс x с использованием инструкций FPU x86 ( x87 ) .

// Вычислить тангенс x real tan ( real x ) { asm { fld x [ EBP ] ; // загрузить x fxam ; // проверить на наличие странных значений fstsw AX ; sahf ; jc trigerr ; // C0 = 1: x - это NAN, бесконечность или пусто // 387-е могут обрабатывать ненормальные значения SC18 : fptan ; fstp ST ( 0 ) ; // вывести X, который всегда равен 1 fstsw AX ; sahf ; // если (!(fp_status & 0x20)) перейти к Lret jnp Lret ; // C2 = 1: x выходит за пределы диапазона, выполнить сокращение аргумента fldpi ; // загрузить pi fxch ; SC17 : fprem1 ; // напоминание (частичное) fstsw AX ; sahf ; jp SC17 ; // C2 = 1: частичное напоминание, нужно зациклить fstp ST ( 1 ) ; // удалить pi из стека jmp SC18 ; } trigerr : return real . nan ; Lret : // Нет необходимости вручную возвращать что-либо, так как значение уже находится в стеке FP ; }                                                                  

Для читателей, незнакомых с программированием x87, fstsw-sahf , за которым следует идиома условного перехода, используется для доступа к битам C0 и C2 слова состояния FPU x87. fstsw сохраняет статус в регистре общего назначения; sahf устанавливает регистр FLAGS в старшие 8 бит регистра; а переход используется для оценки любого бита флага, который соответствует биту состояния FPU. [14]

Ссылки

  1. ^ ab "DontUseInlineAsm". GCC Wiki . Получено 21 января 2020 г.
  2. ^ Striegel, Ben (13 января 2020 г.). ""Для компилятора фрагмент встроенного ассемблера — это как пощечина."". Reddit . Получено 15 января 2020 г. .
  3. ^ C++, [dcl.asm]
  4. ^ ab "Extended Asm - Assembler Instructions with C Expression Operands". Использование компилятора GNU C. Получено 15 января 2020 г.
  5. ^ ab "Встроенный ассемблер". docs.microsoft.com .
  6. ^ «Руководство по миграции и совместимости: встроенная сборка с помощью Arm Compiler 6».
  7. ^ ab d'Antras, Amanieu (13 декабря 2019 г.). "Rust RFC-2873: stable inline asm" . Получено 15 января 2020 г. . Однако можно реализовать поддержку встроенного ассемблера без поддержки со стороны бэкэнда компилятора, используя вместо этого внешний ассемблер.Запрос на отслеживание статуса
  8. ^ "⚙ D54891 [RFC] Проверка встроенного ассемблера на валидность". reviews.llvm.org .
  9. ^ "LLVM Language Reference: Inline Assembly Expressions". Документация LLVM . Получено 15 января 2020 г.
  10. ^ "Inline Assembly". Документация Rust (1.0.0) . Получено 15 января 2020 г.
  11. ^ "Inline Assembler". Язык программирования D. Получено 15 января 2020 г.
  12. ^ "LDC inline assemble expressions". D Wiki . Получено 15 января 2020 г.
  13. ^ syscall(2)  –  Руководство программиста Linux – Системные вызовы
  14. ^ "FSTSW/FNSTSW — Store x87 FPU Status Word". Форма FNSTSW AX инструкции используется в основном в условном переходе...

Внешние ссылки