Jump to content

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

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

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

[ редактировать ]

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

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

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

Синтаксис

[ редактировать ]

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

[ редактировать ]

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

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

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

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

[ редактировать ]

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

Обычно существует два типа встроенной сборки, поддерживаемые компиляторами C/C++:

  • асм (или __asm__ ) в GCC . GCC использует прямое расширение правил ISO: шаблон ассемблерного кода записывается в виде строк, при этом входы, выходы и затертые регистры указываются после строк через двоеточия. Переменные C используются напрямую, а имена регистров заключаются в кавычки как строковые литералы. [4]
  • __asm ​​в Microsoft Visual C++ (MSVC), компиляторе Borland/Embarcadero C и его потомках. Этот синтаксис вообще не основан на правилах ISO; программисты просто пишут ASM внутри блока без необходимости соблюдения синтаксиса C. Переменные доступны так же, как если бы они были регистрами, и разрешены некоторые выражения C. [5] Раньше в ARM Compiler была аналогичная возможность. [6]

Два семейства расширений представляют собой различное понимание разделения труда при обработке встроенной сборки. Форма 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 . Такие вызовы обычно записываются с помощью макросов; полный код включен для ясности. В этом конкретном случае оболочка выполняет системный вызов числа, заданного вызывающей стороной, с тремя операндами, возвращая результат. [13]

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

extern int errno;

int syscall3(int num, int arg1, int arg2, int arg3)
{
  int res;
  __asm__ (
    "int $0x80"        /* make the request to the OS */
    : "=a" (res),      /* return result in eax ("a") */
      "+b" (arg1),     /* pass arg1 in ebx ("b") [as a "+" output because the syscall may change it] */
      "+c" (arg2),     /* pass arg2 in ecx ("c") [ditto] */
      "+d" (arg3)      /* pass arg3 in edx ("d") [ditto] */
    : "a"  (num)       /* pass system call number in eax ("a") */
    : "memory", "cc",  /* announce to the compiler that the memory and condition codes have been modified */
      "esi", "edi", "ebp"); /* these registers are clobbered [changed by the syscall] too */

  /* The operating system will return a negative value on error;
   * wrappers return -1 on error and set the errno global variable */
  if (-125 <= res && res < 0) {
    errno = -res;
    res   = -1;
  }
  return res;
}

Инструкция, специфичная для процессора, в D

[ редактировать ]

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

// Compute the tangent of x
real tan(real x)
{
   asm
   {
       fld     x[EBP]                  ; // load x
       fxam                            ; // test for oddball values
       fstsw   AX                      ;
       sahf                            ;
       jc      trigerr                 ; // C0 = 1: x is NAN, infinity, or empty
                                         // 387's can handle denormals
SC18:  fptan                           ;
       fstp    ST(0)                   ; // dump X, which is always 1
       fstsw   AX                      ;
       sahf                            ; // if (!(fp_status & 0x20)) goto Lret
       jnp     Lret                    ; // C2 = 1: x is out of range, do argument reduction
       fldpi                           ; // load pi
       fxch                            ;
SC17:  fprem1                          ; // reminder (partial)
       fstsw   AX                      ;
       sahf                            ;
       jp      SC17                    ; // C2 = 1: partial reminder, need to loop 
       fstp    ST(1)                   ; // remove pi from stack
       jmp     SC18                    ;
   }
trigerr:
   return real.nan;
Lret:                                    // No need to manually return anything as the value is already on FP stack
   ;
}

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

  1. ^ Перейти обратно: а б «НеИспользоватьИнлайнАсм» . GCC Wiki . Проверено 21 января 2020 г.
  2. ^ Стригель, Бен (13 января 2020 г.). " "Для компилятора кусок встроенного ассемблера - это как пощечина." " . Реддит . Проверено 15 января 2020 г. .
  3. ^ С++, [dcl.asm]
  4. ^ Перейти обратно: а б «Расширенный Asm — инструкции ассемблера с операндами-выражениями C» . Использование компилятора GNU C. Проверено 15 января 2020 г. .
  5. ^ Перейти обратно: а б «Встроенный ассемблер» . docs.microsoft.com .
  6. ^ «Руководство по миграции и совместимости: встроенная сборка с помощью Arm Compiler 6» .
  7. ^ Перейти обратно: а б д'Антрас, Аманье (13 декабря 2019 г.). «Rust RFC-2873: стабильный встроенный ассемблер» . Проверено 15 января 2020 г. . Однако можно реализовать поддержку встроенной сборки без поддержки со стороны серверной части компилятора, используя вместо этого внешний ассемблер. Запрос на включение для отслеживания статуса
  8. ^ «⚙ D54891 [RFC] Проверка валидности встроенной сборки» . Reviews.llvm.org .
  9. ^ «Справочник по языку LLVM: встроенные ассемблерные выражения» . Документация ЛЛВМ . Проверено 15 января 2020 г. .
  10. ^ «Встроенная сборка» . Документация по Rust (1.0.0) . Проверено 15 января 2020 г. .
  11. ^ «Встроенный ассемблер» . Язык программирования D. Проверено 15 января 2020 г. .
  12. ^ «Встроенные ассемблерные выражения LDC» . Д Вики . Проверено 15 января 2020 г. .
  13. ^ syscall(2) Linux программиста Руководство – Системные вызовы
  14. ^ «FSTSW/FNSTSW — сохранение слова состояния FPU x87» . Форма инструкции FNSTSW AX используется в основном при условном ветвлении...
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: a76a9a3bd510c80bb611baf31903d5b5__1718822640
URL1:https://arc.ask3.ru/arc/aa/a7/b5/a76a9a3bd510c80bb611baf31903d5b5.html
Заголовок, (Title) документа по адресу, URL1:
Inline assembler - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)