Ада-95. Компилятор GNAT

       

Параметры ввода


До сих пор мы рассматривали программы на встроенном ассемблере, которые способны осуществлять только вывод информации.

Однако также часто возникает необходимость в осуществлении ввода информации в программу которая написана на встроенном ассемблере.

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

with Interfaces; use Interfaces; with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code;

procedure Inc_It is

function Increment (Value : Unsigned_32) return Unsigned_32 is

Result : Unsigned_32; begin

Asm ("incl %0", Inputs => Unsigned_32'Asm_Input ("a", Value), Outputs => Unsigned_32'Asm_Output ("=a", Result)); return Result; end Increment;

Value : Unsigned_32;

begin

Value := 5; Put_Line ("Value before is" & Value'Img); Value := Increment (Value); Put_Line ("Value after is" & Value'Img); end Inc_It;

Следует заметить, что на данном этапе принцип кодирования инструкций встроенного ассемблера уже должен быть понятен:

Asm ("incl %0", Inputs => Unsigned_32'Asm_Input ("a", Value), Outputs => Unsigned_32'Asm_Output ("=a", Result));

Как видно из этого примера, параметр вывода описан так же как и ранее, определяя, что результат, полученный в регистре eax, будет сохранен в переменной Result.

Описание параметра ввода во многом похоже на описание параметра вывода, но использует атрибут 'Asm_Input вместо атрибута 'Asm_Output.

Кроме того, при описании параметра ввода, отсутствует указание ограничения =, указывающего на вывод значения.

Также как и в случае параметров вывода, допускается наличие множества параметров ввода.

Отсчет параметров (%0, %1, ...) начинается с первого параметра ввода и продолжается при перечислении инструкций вывода.




Следует заметить, что в специальных случаях, когда оба параметра используют один и тот же регистр процессора, компилятор будет рассматривать их как один и тот же %x операнд (например, как в этом случае).
Не трудно догадаться, что если параметр вывода сохраняет значение регистра в переменной назначения после выполнения инструкций ассемблера, то параметр ввода используется для загрузки значения переменной в регистр процессора до начала выполнения инструкций ассемблера.
Таким образом, следующий фрагмент кода:

Asm ("incl %0", Inputs => Unsigned_32'Asm_Input ("a", Value), Outputs => Unsigned_32'Asm_Output ("=a", Result));

указывает компилятору:
  • загрузить 32-битное значение переменной Value в регистр eax
  • выполнить инструкцию incl %eax
  • сохранить содержимое регистра eax в переменной Result
    Если посмотреть на сгенерированный компилятором файл ассемблера (используя полную оптимизацию), то можно обнаружить следующее:

    _inc_it__increment.1: subl $4,%esp movl 8(%esp),%eax #APP incl %eax #NO_APP movl %eax,%edx movl %ecx,(%esp) addl $4,%esp ret

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

    Содержание раздела