Дизайн компилятора - среда выполнения
Программа как исходный код - это просто набор текста (кода, операторов и т. Д.), И для его оживления требуется выполнение действий на целевой машине. Программе требуются ресурсы памяти для выполнения инструкций. Программа содержит имена для процедур, идентификаторов и т. Д., Которые требуют сопоставления с фактической ячейкой памяти во время выполнения.
Под средой выполнения мы понимаем выполняемую программу. Среда выполнения - это состояние целевой машины, которое может включать в себя программные библиотеки, переменные среды и т. Д. Для предоставления услуг процессам, запущенным в системе.
Система поддержки времени выполнения - это пакет, в основном генерируемый самой исполняемой программой и облегчающий взаимодействие процесса между процессом и средой выполнения. Он заботится о выделении и освобождении памяти во время выполнения программы.
Деревья активации
Программа - это последовательность инструкций, объединенных в несколько процедур. Инструкции в процедуре выполняются последовательно. У процедуры есть начальный и конечный разделители, и все, что находится внутри нее, называется телом процедуры. Идентификатор процедуры и последовательность конечных инструкций внутри него составляют тело процедуры.
Выполнение процедуры называется ее активацией. Запись активации содержит всю необходимую информацию, необходимую для вызова процедуры. Запись активации может содержать следующие единицы (в зависимости от используемого исходного языка).
Временники | Хранит временные и промежуточные значения выражения. |
Местные данные | Хранит локальные данные вызываемой процедуры. |
Статус машины | Сохраняет состояние машины, такое как регистры, счетчик программ и т. Д., До вызова процедуры. |
Ссылка управления | Хранит адрес записи активации вызывающей процедуры. |
Ссылка для доступа | Хранит информацию о данных, выходящих за пределы локальной области. |
Фактические параметры | Хранит фактические параметры, т. Е. Параметры, которые используются для отправки ввода в вызываемую процедуру. |
Возвращаемое значение | Сохраняет возвращаемые значения. |
Всякий раз, когда процедура выполняется, ее запись активации сохраняется в стеке, также известном как стек управления. Когда процедура вызывает другую процедуру, выполнение вызывающего объекта приостанавливается до тех пор, пока вызываемая процедура не завершит выполнение. В это время в стеке хранится запись активации вызываемой процедуры.
Мы предполагаем, что управление программой протекает последовательно, и когда процедура вызывается, ее управление передается вызываемой процедуре. Когда вызываемая процедура выполняется, она возвращает управление вызывающей стороне. Этот тип потока управления упрощает представление серии активаций в виде дерева, известного какactivation tree.
Чтобы понять эту концепцию, возьмем в качестве примера фрагмент кода:
. . .
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;
}
. . .
Ниже представлено дерево активации данного кода.
Теперь мы понимаем, что процедуры выполняются в первую очередь в глубину, поэтому выделение стека является наиболее подходящей формой хранения для активации процедур.
Распределение памяти
Среда выполнения управляет требованиями к оперативной памяти для следующих объектов:
Code: Это текстовая часть программы, которая не изменяется во время выполнения. Его требования к памяти известны во время компиляции.
Procedures: Их текстовая часть статична, но они вызываются случайным образом. Вот почему хранилище стека используется для управления вызовами процедур и активациями.
Variables: Переменные известны только во время выполнения, если они не являются глобальными или постоянными. Схема распределения памяти кучи используется для управления выделением и освобождением памяти для переменных во время выполнения.
Статическое размещение
В этой схеме распределения данные компиляции привязаны к фиксированной ячейке памяти и не изменяются при выполнении программы. Поскольку требования к памяти и места хранения известны заранее, пакет поддержки времени выполнения для выделения и отмены выделения памяти не требуется.
Размещение стека
Вызов процедур и их активация управляются посредством выделения памяти стека. Он работает в методе «последним вошел - первым вышел» (LIFO), и эта стратегия распределения очень полезна для рекурсивных вызовов процедур.
Выделение кучи
Переменные, локальные для процедуры, выделяются и отменяются только во время выполнения. Выделение кучи используется для динамического выделения памяти переменным и возврата ее обратно, когда переменные больше не требуются.
За исключением статически выделенной области памяти, память стека и кучи может динамически и неожиданно расти и сокращаться. Поэтому им не может быть предоставлен фиксированный объем памяти в системе.
Как показано на изображении выше, текстовой части кода выделяется фиксированный объем памяти. Память стека и кучи организована по предельным значениям общей памяти, выделенной программе. Оба сжимаются и растут друг против друга.
Передача параметров
Среда связи между процедурами называется передачей параметров. Значения переменных из вызывающей процедуры передаются вызываемой процедуре некоторым механизмом. Прежде чем двигаться дальше, сначала просмотрите некоторые основные термины, относящиеся к значениям в программе.
r-значение
Значение выражения называется его r-значением. Значение, содержащееся в одной переменной, также становится r-значением, если оно отображается справа от оператора присваивания. r-значения всегда могут быть присвоены какой-либо другой переменной.
l-значение
Место в памяти (адрес), где хранится выражение, называется l-значением этого выражения. Он всегда появляется слева от оператора присваивания.
Например:
day = 1;
week = day * 7;
month = 1;
year = month * 12;
Из этого примера мы понимаем, что постоянные значения, такие как 1, 7, 12, и переменные, такие как день, неделя, месяц и год, имеют r-значения. Только переменные имеют l-значения, так как они также представляют назначенную им ячейку памяти.
Например:
7 = x + y;
является ошибкой l-значения, поскольку константа 7 не представляет какой-либо ячейки памяти.
Формальные параметры
Переменные, которые принимают информацию, передаваемую вызывающей процедурой, называются формальными параметрами. Эти переменные объявлены в определении вызываемой функции.
Фактические параметры
Переменные, значения или адреса которых передаются в вызываемую процедуру, называются фактическими параметрами. Эти переменные указываются в вызове функции как аргументы.
Example:
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-значения фактических параметров.
Example:
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, предоставляют новый вид механизма передачи параметров, который работает как препроцессор в языке C. В механизме передачи по имени имя вызываемой процедуры заменяется ее фактическим телом. Передача по имени текстуально заменяет выражения аргументов в вызове процедуры на соответствующие параметры в теле процедуры, так что теперь она может работать с фактическими параметрами, так же как передача по ссылке.