Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

ВВЕДЕНИЕ 5 страница. Рис. 2.2. Регистры общего назначения в системе команд х86



Рис. 2.2. Регистры общего назначения в системе команд х86

У современных процессоров количество регистров измеряется сотнями, а иногда и тысячами. Например, вместо буфера на одну только текущую команду, все без исключения современные процессоры имеют так называемую очередь предварительной выборки команд. У микроконтроллеров PIC и Atmel то, что в спецификациях указано как ОЗУ (у младших моделей 128 или 256 байт, а у старших — много килобайт), фактически представляет собой регистры.

Регистровое окно SPARC
RISC-процессор SPARC [www.sparc.com v9] имеет регистровый файл, объем которого у старых версий процессора составлял 136 32-разрядных слов, а у современных 520 и более. Этот файл состоит из групп по 8 регистров.
Программе одновременно доступно 32 регистра, нумеруемые от 0 до 31, называемые регистровым окном. Из них регистр 0 (обозначаемый как % g0) — выделенный, чтение из него всегда возвращает 0. Следующие 7 регистров (обозначаемые как %g1-%g7) используются для глобальных переменных. Эти регистры не входят в регистровое окно. Регистры с r8 по r15 (%о0-%о7) используются для передачи параметров вызываемой процедуре (r15 используется для хранения адреса возврата подпрограммы), регистры с г24 по г31 (%i0-%i7)— для доступа к параметрам, и, наконец, регистры с г16 по г23 (%I0-%I7) — для хранения локальных переменных. При вызове подпрограммы, команда save сдвигает регистровое окно, так что регистры %оО-%о7 вызывающей процедуры становятся регистрами %о0-%о7 процедуры вызванной. Регистры rО-r7 сдвигом регистрового окна не затрагиваются.
Таким образом, регистровый файл выполняет функции стека вызовов — в нем сохраняются адреса возврата, передаются параметры и хранятся локальные переменные. Впрочем, такой подход при всей его привлекательности имеет большой недостаток— глубина стека вызовов оказывается ограничена глубиной регистрового файла. Способ устранения этого ограничения описан в разд. Косвенный регистровый режим со смещением.

Важно еще раз отметить, "что обычно далеко не все имеющиеся регистры непосредственно доступны программе. Так, в системе команд х86 предусмотрено всего восемь регистров общего назначения, но современные суперскалярные реализации этой архитектуры (Pentium II, Athlon и т. д.) имеют более сотни физических регистров и несколько арифметико-логических устройств. Логика предварительной выборки дешифрует несколько команд и назначает исполнение этих команд на свободные АЛУ, по мере их поступления, с использованием свободных копий регистров, при этом, не нарушая связи по данным между последовательными командами.
При наличии большого количества регистров существует два подхода к их выбору. Первый состоит в том, что каждый код операции работает только со
СВОИМ регистром. Команда move %eax, %ebx — это одна команда, a move
%еах, %esp — совсем другая. При этом бывает и так, что некоторые команды Для некоторых регистров не определены.
Второй подход, более симпатичный автору, называется ортогональной системой команд, и состоит в том, что все команды (или хотя бы большинство) могут работать с любыми регистрами. Номер регистра при этом кодируется специальным битовым полем в коде команды (или несколькими битовыми полями, если команда оперирует несколькими регистрами) (рис. 2.3).

Рис. 2.3. Формат команды микропроцессора SPARC (цит. по [www.sparc.com v9])

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

Адресация оперативной памяти

С точки зрения процессора, оперативная память представляет собой массив пронумерованных ячеек. Номер каждой ячейки памяти называется ее адресом. Разрядность адреса является одной из важнейших характеристик процессора и реализуемой им системы команд. Разрядность важна не как самоцель, а потому, что ею обусловлен объем адресуемой памяти — адресного пространства. Системы с 16-разрядным адресом способны адресовать 64 Кбайт (65 536) ячеек памяти, а с 32-разрядным — 4 Гбайт (4 294 967 296) ячеек. В наше время адресуемая память в 4 Гбайт для многих приложений считается неприемлемо маленькой и требуется 64-разрядная адресация.
Процессору обычно приходится совершать арифметические операции над адресами, поэтому разрядность адреса у современных процессоров обычно совпадает с разрядностью основного АЛУ.
У некоторых компьютеров адресация (нумерация) ячеек памяти фиксированная: одна и та же ячейка памяти всегда имеет один и тот же номер. Такая адресация называется физической. Адрес при этом разбит на битовые поля, которые непосредственно используются в качестве номера физической микросхемы памяти, и номеров строки и столбца в этой микросхеме. Напротив, большинство современных процессоров общего назначения используют виртуальную адресацию, когда номер конкретной ячейки памяти определяется не физическим размещением этой ячейки, а контекстом, в котором происходит адресация. Способы реализации виртуальной памяти и необходимость ее применения обсуждаются в главе 5.
В старых компьютерах размер адресуемой ячейки памяти данных совпадал с разрядностью АЛУ центрального процессора и разрядностью шины данных. Адресуемая ячейка называлась словом. В процессорах манчестерской архитектуры, которые могут использовать одну и ту же память как для команд, так и для данных, оба размера определялись длиной команды. Из-за этого многие процессоры такого типа имели странные по современным представлениям разрядности — 48, 36, иногда даже 25 бит.

БЭСМ-6
Так, БЭСМ-6 имела слово разрядностью 48 бит и команды длиной 24 бита, состоявшие из 15-разрядного адресного поля и 9-разрядного кода операции. Адресное поле позволяло адресовать 32К слов. В одном слове размещалось две команды, при этом команды перехода могли указывать только на первую из упакованных в одно слово команд.

У процессоров гарвардской архитектуры (имеющих раздельные памяти для команд и данных) разрядность АЛУ и размер команды не связаны.

Microchip PIC
Микроконтроллеры семейства PIC фирмы Microchip имеют 8-разрядное АЛУ и накристалльное ОЗУ той же разрядности [www.microchip.com PICMicro]. Команды этих микроконтроллеров размещаются в ПЗУ (также накристалльном), в котором каждое слово имеет 12 бит и содержит одну команду. Аналогично БЭСМ-6, команда микроконтроллера состоит из адресного поля (которое может содержать как адрес, так и константное значение длиной 1 байт) и кода операции. Под код операции остается всего четыре бита, поэтому команд, имеющих полное адресное поле, может быть не более 16. Адресное пространство микроконтроллера составляет 8 бит, т. е. всего 256 слов кода и 256 байт данных. Однако при помощи ухищрения, называемого банковой адресацией (подробнее об этом см. в разд. Банки памяти), старшие модели семейства PIC адресуют и по несколько килобайтов как кода, так и данных.
Впрочем, для многих целей и 256 команд вполне достаточно. Самый маленький в мире Web-сервер [www-ccs.cs.umass.edu] реализован именно на основе PIC. В 512 слов кода удалось упаковать реализацию полноценных подмножеств протоколов RS232 (использованная модель микроконтроллера не имеет аппаратно реализованного последовательного порта), РРР, TCP/IP и HTTP. Web-сервер состоит из двух микросхем — собственно сервера и кристалла флэш-памяти, в котором хранятся экспортируемые сервером HTML-документы и изображения. Сервер включается в последовательный порт компьютера и получает питание от этого порта.
Разработчик сервера рекламирует его как основу (или, во всяком случае, как демонстрацию технической возможности) создания Web-интерфейсов для разнообразного бытового оборудования.

В современных компьютерах единицей адресации является байт — ячейка памяти размером 8 бит. При этом и АЛУ, и шина данных могут иметь большую разрядность (хотя, конечно, эта разрядность всегда кратна байту -8, 16, 32 или 64 бита). Разрядность АЛУ и шины данных может не совпадать: так, 18088 имел 16-разрядное АЛУ и 8-разрядную шину, MC68000 и 180386SX — 32-разрядное АЛУ и 16-разрядную шину. Что при этом понимается под "словом" - зависит от фантазии изготовителей процессора. Например, у вполне 32-разрядного VAX документация называет 16-битовую структуру словом, а 32-битовую — двойным или- длинным словом, хотя, обычно, длиной слова считается все-таки разрядность АЛУ.
При этом различные процессоры по-разному подходят к адресации слов.
Во-первых, некоторые процессоры (PDP-11, SPARC) запрещают обращения к словам, адрес которых не кратен размеру слова, и генерируют при попытках такого обращения исключительную ситуацию ошибки шины. Другие процессоры, например VAX и х86 такие обращения разрешают, но в документации есть честное предупреждение, что обращения к невыровненным словам будут минимум в два раза медленнее, чем к выровненным (рис. 2.4).

Рис. 2.4. Выровненные и невыровненные обращения к словам

Во-вторых, есть разногласия в том, как следует адресовать (или, как следует интерпретировать при арифметических операциях) байты одного слова — по какому адресу находятся старшие 8 битов, а по какому — младшие? У одних Процессоров (IBM/390, MC68000, SPARC) — старший байт располагается по меньшему адресу (big endian), у других (VAX, x86) - по большему (little etidian). Встречаются и процессоры со смешанным порядком байтов, когда, например, из двух байтов полуслова по меньшему адресу расположен младший, а из двух полуслов по меньшему адресу расположено старшее (рис. 2.5). Некоторые современные процессоры, например PowerPC, MIPS, SPARC V9 могут работать и с тем, и с другим порядком байтов, причем иногда даже в пределах одной программы.

Рис. 2.5. Порядок байтов в слове

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

Режимы адресации

Операнды команд могут быть как регистрами, так и ячейками памяти. Некоторые архитектуры, например PDP-11 и VAX, допускают произвольное сочетание регистров и ячеек памяти в одной команде. В частности, допустимы команды пересылки из памяти в память и арифметические операции, оба (у двухадресной PDP-11) или все три (у трехадресного VAX) операнда которых расположены в памяти. В других архитектурах, например в х86 и МС680хО только один операнд команды может размещаться в памяти, а второй всегда обязан быть регистром (впрочем, оба эти процессора имеют и отдельные команды память-память, например инструкции групповой пересылки данных). У RISC-процессоров арифметические операции разрешены только над регистрами, а для обращений к памяти выделены специальные команды LD (LoaD, загрузить) и ST (Store, сохранить).
В зависимости от подхода, применяемого в конкретной системе команд, архитектуры подразделяются на память-память, регистр-память и регистр-регистр. Архитектура регистр-регистр привлекательна тем, что позволяет сделать длину команды фиксированной (адресное поле могут иметь только команды LD/ST и команды вызова и перехода) и за счет этого упростить работу дешифратора и логики предварительной выборки команд. При небольшой длине адреса (как у старых компьютеров и современных микроконтроллеров) этим преимуществом обладают и архитектуры регистр-память.
Напротив, процессоры с большим адресным пространством и архитектурами память-память и регистр-память вынуждены иметь команды переменной длины. У процессоров VAX длина команды меняется от одного (безадресная команда) до 61 байта (шестиадресная команда, все операнды которой используют самый сложный из допустимых режимов адресации). Впрочем, пределом экстравагантности в этом смысле является Intel 432, команды которого имели длину, некратную байту. Адресация команд в 1432 происходила с точностью до бита.
По мере роста адресного пространства адресные поля команд, обращающихся к памяти, занимают все большую и большую долю кода. Это является дополнительным стимулом к замене, где это возможно, обращений к памяти на обращения к регистрам. Благодаря этому же, код активно использующих регистры RISC-процессоров, несмотря на гораздо большую длину кодов команд (если у х86 наиболее широко используемые операции кодируются двумя байтами, то у типичного RISC все команды имеют длину 4 байта), ненамного превосходит по объему эквивалентный код для CISC-процессоров. По мере перехода к 64-разрядным адресам, выигрыш в объеме кода может стать преимуществом RISC-архитектур.
На основе сказанного выше, у читателя могло сложиться впечатление, что единственным способом указания адреса операнда в памяти является помещение этого адреса в адресное поле команды. В действительности это не так, или, точнее, не всегда так — в зависимости от режима адресации, адрес операнда может вычисляться различными, иногда довольно сложными способами, с учетом значений одного или нескольких регистров, и как с использованием адресного поля, так и без него.
Большинство современных процессоров поддерживает многочисленные режимы адресации. Как и при работе с регистрами, это может реализоваться двумя путями: ортогональным, когда режим адресации кодируется битовым полем в коде команды, и неортогональным, когда различные режимы адресации соответствуют разным командам.
Поскольку различные режимы адресации могут как использовать адресное поле, так и не использовать его, чтобы реализовать ортогональную систему с командами фиксированной длины, нужно проявить незаурядную фантазию.

Режимы адресации VAX
У процессоров VAX операнды команд кодируются одним байтом. Старшие 4 бита операнда указывают режим адресации, младшие— номер регистра. Если режим предполагает использование адресных полей, эти поля следуют за операндом. При некоторых режимах возможно использование нескольких адресных полей и длина одного операнда, таким образом, может доходить до 10 байт (рис. 2.6).

Рис. 2.6. Форматы одно-, двух- и трехадресной команд процессора VAX

Режимы адресации SPARC
У процессоров SPARC адресацию осуществляют лишь четыре группы команд — LD (загрузка слова из памяти в регистр), ST (сохранение значения регистра в памяти), JMPL (переход по указанному адресу и сохранение текущего адреса в регистре) и команды условного перехода. Все остальные команды манипулируют регистрами и константами. Команда длиной 32 бита имеет три битовых поля: два задают 5-разрядные номера регистров, третье — либо регистр, либо 13-разрядное целое число (см. рис. 2.3). Команды LD, SP и JMPL имеют такой же формат и позволяют использовать в качестве адреса либо сумму двух регистров, либо регистра и 13-разрядного значения, интерпретируемого как знаковое число в двоично-дополнительной кодировке. Это перекрывает далеко не все перечисленные далее режимы адресации. Многие распространенные режимы адресации на SPARC приходится реализовать с помощью нескольких команд.

Даже классические полностью ортогональные архитектуры — PDP-11, VAX, MC680xO — имеют по крайней мере одно отклонение от полной ортогональности: режим адресации коротким смещением относительно счетчика команд (см. разд. Адресация с использованием счетчика команд, используемый в этих архитектурах в командах условного перехода и недоступный в других командах.

ырожденные режимы адресации

К этой группе относятся режимы, в которых доступ к операнду не содержит адресации как таковой.
Первым из таких режимов является операнд-регистр. Режим этот концептуально крайне прост и в дополнительных комментариях не нуждается.
Второй режим — операнд-константа. В документаииях по многим процессорам этот режим называют литеральной (literal) и немедленной (immediate) адресацией.
Казалось бы, трудно придумать более простой и жизненно необходимый режим. Однако полноценно реализовать такие операнды можно используя либо команды переменной длины, либо команды, которые длиннее слова (чаще всего это бывает у процессоров гарвардской архитектуры, например, уже упоминавшегося PIC).

Литеральная адресация в системе команд SPARC
Разработчики процессоров, которых не устраивает ни одно из названных условий, вынуждены проявлять фантазию. Так, у RISC-процессоров SPARC и команда, и слово имеют одинаковую длину— 32 бита. Адресное поле такой длины в команде невозможно — не остается места для кода операции. Выход, предложенный разработчиками архитектуры SPARC, при первом знакомстве производит странное впечатление, но, как говорят в таких случаях, "не критикуйте то, что работает".
Трехадресные команды SPARC могут использовать в качестве операндов три регистра или два регистра и беззнаковую константу длиной 13 бит. Если константа, которую мы хотим использовать в операции, умещается в 13 бит, мы можем просто использовать эту возможность. На случай, если значение туда не помещается, предоставляется команда sethi const22, reg, которая имеет 22-разрядное поле и устанавливает старшие биты указанного регистра, равными этому полю, а младшие биты — равными нулю.
Таким образом, если мы хотим поместить в регистр 32-разрядную константу value, мы должны делать это с помощью двух команд: sethi %hi (value), reg; or %gO, %lo (value), reg; (в соответствии с [docs.sun.com 806-3774-10], именно так реализована ассемблерная псевдокоманда set value, reg).
С точки зрения занимаемой памяти, это ничуть не хуже, чем команда set value, reg, которая тоже должна была бы занимать 64 бита. Зато такое решение позволяет соблюсти принцип: одна команда — одно слово, который облегчает работу логике опережающей выборки команд.
Впрочем, для 64-разрядного SPARC v9 столь элегантного решения найдено не было. Способ формирования произвольного 64-битового значения требует дополнительного регистра и целой программы (пример 2.1). В зависимости от значения константы этот код может подвергаться оптимизации. Легче всего, конечно, дело обстоит, если требуемое значение помещается в 13 бит.

Пример 2.1. Формирование 64-разрядного значения на SPARC v9, цит. по [docs.sun.com 806-3774-10]

! reg — промежуточный регистр, rd — целевой.
sethi %uhi(value), reg
or reg, %ulo(value), reg
sllx reg,32,reg! сдвиг на 32 бита
sethi %hi(value), rd
or rd, reg, rd
or rd, %lo(value), rd

"Короткие литералы" разного рода нередко используются и в других процессорах, особенно имеющих большую разрядность. Действительно, большая часть реально используемых констант имеет небольшие значения, и выделение под каждую такую константу 32- или, тем более, 64-разрядного значения привело бы к ненужному увеличению кода.

Короткие VAX
У процессоров семейства VAX есть режим адресации, позволяющий использовать битовое поле, которое в других режимах интерпретируется как номер регистра, в качестве 4-битового литерала. Вместе с двумя битами режима адресации этим способом можно задать 6-разрядный литерал, знаковый или беззнаковый в зависимости от контекста [Прохоров 1990].

Короткие литералы МСбЗОхО
У процессоров семейства МС680хО литерал может иметь длину 1 или 2 байта. Кроме того, предоставляются команды ADDQ и SUBQ, которые позволяют добавить к указанному операнду или вычесть из него целое число в диапазоне от 1 до 8.

Абсолютная адресация

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

Абсолютная адресация в системе команд SPARC
У процессоров SPARC реализация абсолютной адресации похожа на реализацию адресации литеральной: под адресацию занимается регистр, командой sethi %hi (addr), reg в него загружается старшая часть адреса, а затем происходит собственно адресация. Для формирования 64-разрядного адреса необходимо занимать два регистра и выполнять ту же программу, что и в примере 2.1.
Обращение к переменной в памяти происходит так, как показано в примере 2.2.

Пример 2.2. Обращение к переменной на процессоре SPARC

sethi %hi(var), %g1 ! помещаем старшие биты адреса в %g1
Id [%gl+%lo(var)], %11 ! загружаем значение в %11
inc %11 ! производим операцию
st %11, [%gl+%lo(var)] ! сохраняем результат.

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

Косвенно-регистровый режим

В этом режиме, как и в регистровом, адресное поле не используется. Значение регистра интерпретируется как адрес операнда. Данный режим используется для разыменования указателей или для обращения к памяти по предварительно вычисленному адресу.
Некоторые процессоры, такие, как PDP-11, VAX, МСбЗОхО, имеют любопытные варианты этого режима — адресацию с постинкрементом и предек-рементом. Постинкремент означает, что после собственно адресации значение регистра увеличивается на величину адресуемого объекта. Предекремент, соответственно, означает, что регистр уменьшается на ту же величину перед адресацией.
Эти режимы могут использоваться для разнообразных целей, например для реализации операций над текстовыми строками или поэлементного сканирования массивов. Но одно из основных назначений -- это реализация стека.

Стек

Стек, или магазин — это структура данных, над которой мы можем осуществлять две операции: проталкивание (push) значения и выталкивание (pop). Значения выталкиваются из стека в порядке, обратном тому, в котором проталкивались: LIFO (Last In, First Out, первый вошел, последний вышел). Стековые структуры находят широкое применение при синтаксическом разборе арифметических выражений и алголоподобных языков программирования [Кормен/Лейзерсон/Ривест 2000].
Самая простая реализация стека — это массив и индекс последнего находящегося в стеке элемента (рис. 2.7). Этот индекс называется указателем стека (SP - Stack Pointer). Стек может расти как вверх, так и вниз [(рис. 2.8). Широко применяются также реализации стеков в виде односвяз-|ных списков.

Рис. 2.7. Стек на основе массива

Рис. 2.8. Стеки, растущие вверх и вниз

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

MOVE x, -(SP)

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

MOVE (SP)+, у

приведет к получению значения и продвижению указателя стека в обратном направлении. Поэтому первая команда имеет также мнемоническое обозначение

PUSH х

а вторая

POP у

Если мы поместим несколько значений в стек командой PUSH, команда POP вытолкнет их из стека в обратном порядке. Стек можно использовать для хранения промежуточных данных (см. пример 2.3) и при реализации арифметических выражений — например, команда

ADD (SP)+, (SP)

в точности воспроизводит описанную выше семантику безадресной команды ADD стековой архитектуры. Впрочем, безадресной команде ADD операнды не нужны, а в данном случае они просто не используются, но никуда не исчезают. Команда получается длиннее: у типичной стековой архитектуры команда сложения занимает 1 байт, у PDP-11 ее имитация занимает 2 байта, а у VAX — целых три. Поэтому, если мы хотим использовать стековую технику генерации кода, лучше использовать предназначенный для этого процессор.

Пример 2.3. Использование стека для хранения промежуточных значений

void swap(int &a, int &b) {
int t;
t=a;
a=b;
b=t;
}

;Для простоты мы не рассматриваем механизм передачи параметров
; и считаем, что они передаются в регистрах А и В
.GLOBL swap
swap:
POSH (A)
MOVE (B), (A)
POP (B)
RET.

Одно из основных назначений стека в регистровых архитектурах — это сохранение адреса возврата подпрограмм. Кроме того, если принятое в системе соглашение о вызовах подпрограмм предполагает, что вызываемая процедура должна сохранить все или некоторые регистры, которые использует сама, стек обычно применяют и для этого.
Неортогональные процессоры, такие, как х86, часто предоставляют специальные команды PUSH и POP, работающие с выделенным регистром SP (Stack Pointer), который не может быть использован для других целей.

Косвенно-регистровый режим со смещением

Адрес операнда образуется путем сложения регистра и адресного поля команды. Этот режим наиболее богат возможностями и, в зависимости от стиля использования, имеет много других названий, например базовая адресация или индексная адресация. Адресное поле необязательно содержит полноценный адрес и может быть укороченным.
Команды id/st процессора SPARC, используемые в примере 2.2, реализуют косвенно-регистровую адресацию с 13-разрядным смещением.
Возможные варианты использования этого режима адресации многочисленны. Например, если смещение представляет собой абсолютный адрес начала массива, а в регистре хранится индекс, этот режим может использоваться для индексации массива. В этом случае смещение должно представлять собой полноценный адрес.
3 другом случае, в регистре может храниться указатель на структуру Данных, а смещение может означать смещение конкретного поля относительно начала структуры. Еще один вариант -- регистр хранит указатель на стековый кадр или блок параметров процедуры, а смещение -адрес локальной переменной в этом кадре или определенного параметра.
В этих случаях можно использовать (и обычно используется) укороченное смещение.

Стековый кадр

Стековый кадр является стандартным способом выделения памяти под локальные переменные в алголоподобных процедурных языках (С, C++, Pascal) и других языках, допускающих рекурсивные вызовы.
Семантика рекурсивного вызова в алголоподобных языках требует, чтобы каждая из рекурсивно вызванных процедур имела собственную копию локальных переменных. В SPARC это достигается сдвигом регистрового окна по регистровому файлу (рис. 2.9), но большинство других процессоров такой роскоши лишены и вынуждены выделять память под локальные переменные в стеке, размещаемом в ОЗУ.

Рис. 2.9. Регистровый стек процессора SPARC

Для этого вызванная процедура уменьшает (если стек растет вниз) указатель стека на количество байтов, достаточное, чтобы разместить переменные. Адресация этих переменных у некоторых процессоров (например, у PDP-11) происходит относительно указателя стека, а у большинства — например, у МС680хО и VAX, с большим количеством регистров или у х86, указатель стека которого нельзя использовать для адресации со смещением — для этой цели выделяется отдельный регистр (рис. 2.10, пример 2.4).

Пример 2.4. Формирование, использование и уничтожение стекового кадра. Код на языке С и результат его обработки GNU С 2.7.2.1 (комментарии автора)

#include <stdio.h>
# include <strings.h>

/* Фрагмент примитивной реализации сервера SMTP (RFC822) */
int parse_line(FILE * socket)
{
/* Согласно RFC822, команда имеет длину не более 4 байт,а вся строка — не более 255 байт V char cmd[5], args [255]; fscanf (socket, "%s %s\n", cmd, args);
if (stricmpfcmd, "HELO")==0) {
fprintf (socket, "200 Hello, %s, glad to meet you\n", args);
return 200;
)
/* etc */
fprintf (socket, "500 Unknown command %s\n", cmd);
return 500;
.file "sample" gcc2_compiled.: _ gnu_compiled_c:.text LCO:
.ascii "%s %s\12\0" LCI:
.ascii "HELCAO" LC2:
.ascii "200 Hello, %s, glad to meet you\12\0" LC3:
.ascii "500 Unknown command %s\12\0"
.align 2, 0x90.globl _parse_line _parse_line:
; x86 имеет для этой цели специальную команду enter, но она может; формировать кадры размером не более 255 байт. В данном случае кадр; имеет больший размер, и его необходимо формировать вручную.
pushl %ebp; Сохраняем указатель кадра
; вызвавшей нас подпрограммы.
movl %esp, %ebp; Формируем указатель нашего кадра
subl $264,%esp; И сам кадр.; Конец пролога функции
leal -264 (%ebp), %еах; Помещаем в стек указатель на args • pushl %eax
leal -8 (%ebp), %еах;... на cmd
pushl %eax
pushl $LCO; на строковую константу
; Наши собственные параметры тоже адресуются относительно кадра. movl 8(%ebp),%eax; Параметр socket мы тоже проталкиваем pushl %eax; в стек
call fscanf; Вызов (параметры в стеке) addl $16,%esp; очищаем стек
; в языке С переменное количество параметров, поэтому вычищать их из
; стека должна вызывающая процедура. Вызываемая просто не знает,
; СКОЛЬКО ИХ бЫЛО.
pushl $LC1
leal -8(%ebp),%eax
pushl %eax
call _stricmp
addl $8,%esp
movl %eax,%eax; выключенная оптимизация в действии:)
; А ведь недалеки времена, когда компиляторы только такое и умели; генерировать.
testl %eax,%еах
jne L14
leal -264(%ebp),%еах
pushl %eax
pushl $LC2
movl 8(%ebp),%eax
pushl %eax
call _fprintf
addl $12,%esp
; Обратите внимание, что компилятор не стал генерировать второй эпилог; функции на втором операторе return.
movl $200,%eax
jmp L13
» Выравнивание потенциальных точек перехода на границу слова полезно:» процессор не будет тратить дополнительный цикл шины на чтение» невыровненной команды. Для выравнивания используется команда NOP; (код операции 0x90).
align 2,0x90 L14:
leal -8(%ebp),%еах
Pushl %eax
Pushl $LC3
movl 8(%ebp),%eax pushl %eax
call _fprintf
addl?12,%esp
movl $500,%eax
jmp L13
.align 2,0x90 L13:
; Команда leave совершает действия, обратные прологу функции:; Она эквивалентна командам: move %ebp, %esp; pop %ebp.; Размер кадра явным образом не указывается, поэтому ограничений; на этот размер в данном случае нет.
leave
ret





Дата публикования: 2014-11-18; Прочитано: 489 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.016 с)...