Проектирование компилятора - среда выполнения

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

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

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

Деревья активации

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

Выполнение процедуры называется ее активацией. Запись активации содержит всю необходимую информацию, необходимую для вызова процедуры. Запись активации может содержать следующие единицы (в зависимости от используемого исходного языка).

временных Хранит временные и промежуточные значения выражения.
Локальные данные Хранит локальные данные вызванной процедуры.
Статус машины Сохраняет состояние машины, такое как регистры, счетчик программ и т. Д., До вызова процедуры.
Контрольная ссылка Хранит адрес записи активации процедуры вызывающей стороны.
Ссылка доступа Хранит информацию о данных, которые находятся за пределами локальной области видимости.
Фактические параметры Сохраняет фактические параметры, то есть параметры, которые используются для отправки ввода в вызываемую процедуру.
Возвращаемое значение Магазины возвращают значения.

Всякий раз, когда процедура выполняется, ее запись активации сохраняется в стеке, также известном как стек управления. Когда процедура вызывает другую процедуру, выполнение вызывающей стороны приостанавливается до тех пор, пока вызываемая процедура не завершит выполнение. В это время запись активации вызванной процедуры сохраняется в стеке.

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

Чтобы понять эту концепцию, мы возьмем кусок кода в качестве примера:

. . .
printf(“Enter Your Name: “);
scanf(“%s”, username);
show_data(username);
printf(“Press any key to continue…”);
. . .
int show_data(char *user)
   {
   printf(“Your name is %s”, username);
   return 0;
   }
. . . 

Ниже приведено дерево активации приведенного кода.

Дерево активации

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

Распределение памяти

Среда выполнения управляет требованиями к памяти во время выполнения для следующих объектов:

  • Код : Он известен как текстовая часть программы, которая не изменяется во время выполнения. Требования к памяти известны во время компиляции.

  • Процедуры : их текстовая часть статична, но они вызываются случайным образом. Вот почему стековое хранилище используется для управления вызовами и активациями процедур.

  • Переменные . Переменные известны только во время выполнения, если они не являются глобальными или постоянными. Схема выделения памяти кучи используется для управления распределением и выделением памяти для переменных во время выполнения.

Статическое Распределение

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

Распределение стека

Вызовы процедур и их активации управляются посредством выделения памяти стека. Он работает в методе «последний пришел первым» (LIFO), и эта стратегия распределения очень полезна для рекурсивных вызовов процедур.

Распределение кучи

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

За исключением статически выделенной области памяти, память стека и кучи может увеличиваться и уменьшаться динамически и неожиданно. Поэтому они не могут быть обеспечены фиксированным объемом памяти в системе.

Распределение кучи

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

Передача параметров

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

г-значение

Значение выражения называется его r-значением. Значение, содержащееся в одной переменной, также становится r-значением, если оно появляется в правой части оператора присваивания. Значения r всегда можно присвоить некоторой другой переменной.

л-значение

Местоположение памяти (адрес), где хранится выражение, называется l-значением этого выражения. Он всегда отображается слева от оператора присваивания.

Например:

day = 1;
week = day * 7;
month = 1;
year = month * 12;

Из этого примера мы понимаем, что постоянные значения, такие как 1, 7, 12, и переменные, такие как день, неделя, месяц и год, все имеют r-значения. Только переменные имеют l-значения, так как они также представляют назначенную им ячейку памяти.

Например:

7 = x + y;

является ошибкой l-значения, поскольку константа 7 не представляет какую-либо ячейку памяти.

Формальные параметры

Переменные, которые принимают информацию, переданную процедурой вызывающей стороны, называются формальными параметрами. Эти переменные объявляются в определении вызываемой функции.

Фактические параметры

Переменные, значения или адреса которых передаются в вызываемую процедуру, называются фактическими параметрами. Эти переменные указываются в вызове функции в качестве аргументов.

Пример:

fun_one()
{
   int actual_parameter = 10;
   call fun_two(int actual_parameter);
}
   fun_two(int formal_parameter)
{
   print formal_parameter;
}

Формальные параметры содержат информацию о фактическом параметре, в зависимости от используемой техники передачи параметров. Это может быть значение или адрес.

Передать по значению

В механизме передачи по значению вызывающая процедура передает r-значение фактических параметров, и компилятор помещает это в запись активации вызываемой процедуры. Затем формальные параметры содержат значения, переданные вызывающей процедурой. Если значения, содержащиеся в формальных параметрах, изменяются, это не должно влиять на фактические параметры.

Передать по ссылке

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

Передача копировать-восстановить

Этот механизм передачи параметров работает аналогично «передаче по ссылке», за исключением того, что изменения в фактических параметрах делаются после завершения вызываемой процедуры. При вызове функции значения фактических параметров копируются в запись активации вызванной процедуры. Формальные параметры, если ими манипулируют, не влияют в реальном времени на фактические параметры (поскольку передаются l-значения), но когда завершается вызванная процедура, l-значения формальных параметров копируются в l-значения фактических параметров.

Пример:

int y; 
calling_procedure() 
{
   y = 10;     
   copy_restore(y); //l-value of y is passed
   printf y; //prints 99 
}
copy_restore(int x) 
{     
   x = 99; // y still has value 10 (unaffected)
   y = 0; // y is now 0 
}

Когда эта функция заканчивается, значение l формального параметра x копируется в фактический параметр y. Даже если значение y изменяется до завершения процедуры, l-значение x копируется в l-значение y, что ведет себя как вызов по ссылке.

Передать по имени

Такие языки, как Algol, предоставляют новый тип механизма передачи параметров, который работает как препроцессор в языке Си. В механизме передачи по имени имя вызываемой процедуры заменяется ее фактическим телом. Передача по имени заменяет выражения аргумента в вызове процедуры на соответствующие параметры в теле процедуры, чтобы теперь она могла работать с фактическими параметрами, подобно передаче по ссылке.