Учебно-методические материалы для студентов кафедры АСОИУ

Ричард Столлман. gcc: компилятор языков C, C++, Objective C (часть 2)

gcc: Компилятор языков C, C++, Objective C (часть 2)
====================================================================
Перенос на новые архитектуры
gcc 2.7
Ричард Столлман (Richard Stallman)
.- 2 -
Содержание
1. GNU CC и переносимость 5
2. Интерфейс вывода в GNU CC 6
3. Проходы и Файлы Транслятора 9
4. Представление RTL 19
4.1. Типы объектов RTL 19
4.2. Доступ к Операндам 20
4.3. Флаги в выражении RTL 24
4.4. Машинные типы 30
4.5. Типы константных выражений 35
4.6. Регистры и память 38
4.7. RTL-Выражения для работы с арифметикой 44
4.8. Операции сравнения 48
4.9. Битовые поля. 50
4.10. Преобразования 51
4.11. Объявления 52
4.12. Выражения с побочным эффектом 53
4.13. Вложенные побочные эффекты на адресах 60
4.14. Команды Ассемблера как выражения 61
4.15. Insns 62
4.16. RTL Представление Insns - Вызовов Функций 74
4.17. Предположения о Совместном Использовании Структур 75
4.18. Чтение RTL 77
5. Машинные Описания 78
5.1. Все про Образцы Команд 78
5.2. Пример " define_insn " 80
5.4. RTL Шаблоны 80
5.5. Шаблоны вывода и замена операндов 87
5.6. Операторы С для вывода ассемблера 89
5.7. Ограничения операндов 91
5.7.1. Простые Ограничения 91
5.7.2. Многократные Альтернативные Ограничения 98
5.7.3. Предпочтения классов регистров 100
5.7.4. Символы модификатора ограничения 100
5.7.5. Ограничения для Специфических Машин 102
5.7.6. Без ограничений 113
- 3 -
5.8. Стандартные Образцы Имен Для Генерации 113
5.9. Когда Порядок Образцов Играет Роль 129
5.10. Взаимозависимость Образцов 130
5.11. Определение Образцов Команд Перехода 132
5.12. Канонизация Команд 135
5.13. Машинно - специфические локальные оптимизации 137
5.14. Определенные RTL последовательности для генерации кода 141
5.15. Как разделять инструкции 145
5.16. Атрибуты команд 149
5.16.1. Определение атрибутов и их значений. 150
5.16.1. Атрибут-выражения 151
5.16.2. Установка значения атрибута для insns. 155
5.16.3. Примеры спецификаций атрибута 157
5.16.4. Вычисление Длины Insn 158
5.16.5. Постоянные атрибуты 161
5.16.6. Планирование слота задержки 161
5.16.7. Определения функциональных модулей 163
6. Target Description Macros 167
6.1. Управление Драйвером Трансляции " gcc " 167
6.2. Целевая Спецификация времени выполнения 174
6.3. Распределение Памяти 178
6.4. Формат Исходных Типов Данных Языка 187
6.4. Использование Регистров 191
6.4.1. Базовые Характеристики Регистров 191
6.4.2. Порядок Распределения Регистров 194
6.4.2. Как Располагать Значения В Регистрах 194
6.4.3. Регистры, образующие стек 198
6.4.4. Устаревшие Макрокоманды для Управления Использованием Реги 199
6.5. Классы Регистров 200
6.6. Формат Стека и Соглашения о вызовах 210
6.6.1.Основные Параметры Стека 210
6.6.2. Регистры, Адресующие Кадр стека 212
6.6.3. Удаление Указателя Кадра и Указателя Аргументов 214
6.6.4. Передача Аргументов Функции через Стек 216
6.6.5. Передача Параметров В Регистрах 219
6.6.6. Как Возвращаются Значения Скалярной Функции 224
6.6.7. Как Возвращаются Структурированные Значения 226
6.6.8. Распределение Регистров с Сохранением 228
- 4 -
6.6.9. Вход и Выход из Функции 229
6.6.10. Генерация Кода для Профилирования 233
6.7. Выполнение Varargs Макрокоманд 235
6.8. Trampolines для вложенных функций 238
6.9. Неявные Обращения к Библиотечным Подпрограммам 242
6.10. Способы адресации 246
6.11. Состояние Кода Условия 250
6.12. Описание Относительных Стоимостей Операций 254
6.13. Деление Вывода на Разделы 258
6.14. Позиционно-Независимый Код 261
6.15. Определение Выходного Языка Ассемблера 262
6.15.1. Полная Структура Ассемблерного Файла 262
6.15.2. Вывод Данных 265
6.15.3. Вывод Неинициализированных Переменных 268
6.15.4. Вывод и Генерация Меток 269
6.15.5. Как обрабатываются функции инициализации 275
6.15.6. Макрокоманды, управляющие программами инициализации 278
6.15.7. Вывод Команд Ассемблера 280
6.15.8. Вывод Таблиц Распределения 285
6.15.9. Команды Ассемблера для Выравнивания 286
6.17. Управление Информационным Форматом отладки 287
6.17.1. Макрокоманды, которые воздействуют на все отладочные форм 287
6.17.2. Специфические Опции для Вывода DBX 289
6.17.3. Открытые Ловушки для Формата DBX 292
6.17.4. Имена Файлов в Формате DBX 294
6.17.5. Макрокоманды для SDB и Вывода DWARF 295
6.18. Кросс-трансляция и числа с плавающей точкой 296
6.19. Смешанные параметры 300
7. Файл конфигурации 311
8. Фрагменты Makefile-а 316
8.1. Фрагменты Makefile, отвечающие целевой машине 316
8.2. Фрагменты Makefile, отвечающие главной машине 318
.- 5 -
1. GNU CC и переносимость
Основная цель GNU CC состояла в создании хорошего, быстрого
транслятора для класса машин, на которых предполагается использование
системы GNU: 32-битные машины, адресующие 8-битные байты и имеющие
несколько регистров общего назначения. Элегантность, теоретическая
мощность и простота стояли только на втором месте.
GNU CC получает большую часть информации относительно целевой
машины из описания машины, которое дает алгебраическую формулу для
каждой машинной команды. Это - очень чистый способ описать целевую
машину. Но когда транслятор нуждается в информации, которую трудно
выразить таким способом, я не колебался определять для данного случая
параметр машинного описания. Цель переносимости - уменьшить общую
работу, необходимую на трансляторе; само по себе это не представляет
интереса.
GNU CC не содержит машинно-зависимого кода, но он содержит код,
который зависит от машинных параметров типа endianness (имеет старший
байт больший или меньший адрес в слове) и доступности адресации с
автоматическим инкрементом. В проход генерации RTL часто необходимо
иметь набор cтратегий для генерации кода для специфического вида
дерева синтаксиса, cтратегий, пригодных для использования при
различных комбинациях параметров. Часто я не пытался охватить все
возможные случаи, а рассматривал только общие или только те, которые я
встречал. В результате новая целевая машина может потребовать
дополнительных cтратегий. Если такое случится, Вы узнаете об этом,
потому что транслятор вызовет " abort ". К счастью, новые cтратегии
могут быть добавлены машинно-независимым способом, и будет действовать
только на тех целевых машинах, на которых они нужны.
.- 6 -
2. Интерфейс вывода в GNU CC
Обычно GNU CC конфигурируется для использования того же самого
соглашения о вызове функций, которое обычно используется в целевой
системе. Это выполнено при помощи макрокоманд с машинным описанием (*
См.: Целевые макрокоманды::.).
Однако, возврат значений структуры и объединения выполнен
по-другому на некоторых целевых машинах. В результате, функции,
скомпилированные PCC, возвращающие такие типы, не могут вызываться из
кода, скомпилированного GNU CC, и наоборот. Но это не вызывает
проблему часто, потому что мало библиотечных подпрограмм UNIX
возвращает структуры или объединения.
GNU CC код возвращает структуры и объединения длины 1, 2, 4 или 8
байт в тех же самых регистрах, которые используются для возвращения
значения " int " или " double ".( GNU CC обычно распределяет
переменные таких типов тоже в регистрах.) Структуры и объединения
других размеров возвращаются путем записи их по адресу, переданному
вызывающей подпрограммой (обычно в регистре). Макрокоманды с машинным
описанием " STRUCT_VALUE " и " STRUCT_INCOMING_VALUE " сообщают GNU
CC, куда передать этот адрес.
Напротив, PCC на большинстве целевых машин возвращает структуры и
объединения любого размера, копируя данные в область статической
памяти, и затем возвращая адрес этого места как указатель на значение.
Вызывающая подпрограмма должна скопировать данные из этой области
памяти туда, куда требуется. Этот метод медленнее, чем используемый
GNU CC, и его нельзя использовать повторно.
На некоторых целевых машинах, типа RISC машин и 80386,
стандартное системное соглашение состоит в передаче подпрограмме
адреса, куда надо возвращать значение. На этих машинах GNU CC
конфигурован для совместимости со стандартным транслятором, когда
используется этот метод. Он не может быть совместим для структур 1, 2,
4 или 8 байтов.
- 7 -
GNU CC использует стандартное соглашение системы для передачи
параметров. На некоторых машинах первые несколько параметров
передаются в регистрах; на других все передаются в стеке. Можно было
бы использовать регистры для передачи параметров на любой машине, и
это, вероятно, дало бы значительное ускорение. Но результатом была бы
полная несовместимость с кодом, построенным по стандартному
соглашению. Так что это изменение можно осуществлять, только если Вы
используете лишь один GNU CC из трансляторов C для системы. Мы сможем
выполнять регистровую передачу параметра на определенных машинах, как
только у нас будет такая полная GNU система, что мы сможем
компилировать библиотеки при помощи GNU CC.
На некоторых машинах (особенно Sparc), некоторые типы параметров
передаются "невидимой ссылкой". Это означает, что значение сохраняется
в памяти, и адрес ее расположения в памяти передается подпрограмме.
Если Вы используете " longjmp ", остерегайтесь автоматических
переменных. ANSI C говорит,что автоматические переменные, которые не
объявлены "volatile", имеют неопределенные значения после " longjmp ".
И это - все, что GNU CC обещает делать, потому что очень трудно
восстановить регистровые переменные правильно, и одна из особенностей
GNU CC - то, он это может помещать переменные в регистры без вашего
требования.
Если Вы хотите, чтобы переменная не изменялась после "longjmp", и
Вы не хотите писать "volatile", потому что старые C трансляторы не
воспринимают это, просто вычислите адрес переменной. Если адрес
переменной когда-либо вычислялся, пусть только для того, чтобы
вычислить его и игнорировать его, то переменная не может входить в
регистр:
{
int careful;
$careful;
...
}
Код, компилируемый GNU CC, может вызывать некоторые библиотечные
- 8 -
подпрограммы. Большинство из них обрабатывают арифметику, для которой
не имеется никаких команд. Это включает умножение и деление на
некоторых машинах, и операции с плавающей точкой на любой машине, на
которой поддержка плавающей точки отключена с помощью "-msoft-float".
Некоторые стандартные части библиотеки C, типа "bcopy" или "memcpy",
также вызываются автоматически. Обычный интерфейс вызова функций
используется для вызова библиотечных подпрограмм.
Эти библиотечные подпрограммы должны быть определены в библиотеке
"libgcc.a", которую GNU CC автоматически ищет всякий раз, когда
линкует программу. На машинах, которые имеют команды умножения и
деления при использовании аппаратной поддержки плавающей точки
"libgcc.a" обычно не необходим, но ищется на всякий случай.
Каждая арифметическая функция определена в " libgcc1.c " для
использования соответствующего арифметического оператора C. Как только
файл будет скомпилирован другим транслятором C, который поддерживает
все арифметические операторы C, этот файл будет работать
машиннонезависимо. Однако, " libgcc1.c " не будет работать, если его
скомпилировать GNU CC, потому что каждая арифметическая функция
скомпилируется в обращение к себе!
.- 9 -
3. Проходы и Файлы Транслятора
Полная структура управления транслятора находится в "toplev.c".
Это файл ответственен за инициализацию, декодирование параметров,
открытие и закрытие файлов и последовательность проходов.
Проход синтаксического анализа вызывается только однажды для
анализа всего ввода. Промежуточный код RTL для функции генерируется,
когда функция анализируется. Обработка происходит пооператорно. Каждый
оператор считывается как синтаксическое дерево и затем преобразуется в
RTL; после этого память, содержащая дерево оператора, освобождается.
Типы (и выражения для их размеров), объявления, представления
связывания контуров и как они вкладываются сохраняются, пока функция
не скомпилируется; все они необходимы, чтобы вывести информацию об
отладке.
Каждый раз, когда при проходе синтаксического анализа читается
полное определение функции или объявление верхнего уровня, вызывается
функция " rest_of_compilation " или функция " rest_of_decl_compilation
" в " toplev.c ", которые ответственны за всю дальнейшую необходимую
обработку, заканчивающуюся выводом на языке ассемблера. Остальные
проходы транслятора вызываются последовательно внутри "
rest_of_compilation ". Когда эта функция возвращается из компиляции
определения функции, память, используемая для трансляции этого
определения, полностью освобождается, если это не встроенная функция
(См.: Встроенная функция работает так же быстро, как Макрокоманда:
Встраивание.).
Вот список всех проходов транслятора и их исходных файлов. Также
включено описание того, как запросить отладочные дампы при помощи
опции " -d ".
* Синтаксический анализ. Этот проход читает весь текст
определения функции и строит частичные синтаксические деревья. Это и
генерация RTL не являются больше отдельными проходами (как раньше), но
проще думать о них как об отдельных.
- 10 -
Представление дерева не следует полностью синтаксису C, потому
что оно предназначено поддерживать также другие языки.
Специфический для языка анализ типа данных также выполнен в этом
проходе, и к каждому узлу дерева, который представляет выражение,
присоединен тип его данных. Переменные представляются как
узлы-объявления.
Вычисление констант и некоторые арифметические упрощения также
выполняются во время этого прохода.
Языконезависимые исходные файлы для синтаксического анализа -
"stor-layout.c", "fold-const.c", и "tree.c". Есть также файлы
заголовка "tree.h" и "tree.def", которые определяют формат
представления дерева.
Исходные файлы для анализа C - "c-parse.in", "c-decl.c",
"c-typeck.c", "c-aux-info.c", "c-convert.c" и "c-lang.c", а также
файлы заголовка "c-lex.h", и "c-tree.h".
Исходные файлы для анализа C ++ - "cp-parse.y", "cp-class.c",
"cp-cvt.c", "cp-decl.c", "cp-decl2.c", "cp-dem.c", "cp-except.c",
"cp-expr.c", "cp-init.c", "cp-lex.c", "cp-method.c", "cp-ptree.c",
"cp-search.c", "cp-tree.c", "cp-type2.c", и "cp-typeck.c", а также
файлы заголовка "cp-tree.def", "cp-tree.h", и "cp-decl.h".
Специальные исходные файлы для синтаксического анализа Objective
C "objc-parse.y", "objc-actions.c", "objc-tree.def", и
"objc-actions.h". Для этого используются также некоторые специфические
файлы C.
Файл "c-common.c" также используется для всех вышеуказанных
языков.
* Генерация RTL. Это преобразование синтаксического дерева в код
RTL. Фактически она выполняется пооператорно во время синтаксического
анализа, но для большинства целей о ней можно думать как об отдельном
проходе.
- 11 -
Именно здесь находится большая часть кода, зависящего от машины,
так для cтратегий часто бывает необходимо применять их только тогда,
когда доступны определенные стандартные виды команд. Цель названных
образцов команд - обеспечить эту информацию для прохода генерации RTL.
В этом проходе выполнена оптимизация для "if"-условий, являющихся
сравнениями, булевыми операциями или условными выражениями.
"Хвостовая" рекурсия также распознается в это время. Принимаются
решения о том, как лучше всего упорядочивать циклы и как выводить
операторы "switch".
Исходные файлы для генерации RTL включают "stmt.c", "calls.c",
"expr.c", "explow.c", "expmed.c", "function.c", "optabs.c" и
"emit-rtl.c". Также в этом проходе используется файл "insn-emit.c",
сгенерированный из машинного описания программой "genemit". Файл
заголовка "expr.h" используется для связи внутри этого прохода.
Файлы заголовка "insn-flags.h" и "insn-codes.h", сгенерированные
из машинного описания программами " genflags " и " gencodes ",
сообщают этому проходу, какие стандартные имена доступны для
использования и какие образцы соответствуют им.
Кроме вывода информации об отладке, ни один из следующих проходов
не обращается к представлению структуры дерева функции (а только к той
его части, которая сохранена).
Решение о том, может ли и должен ли код функции напрямую
встраиваться в код функций, ее вызывающих, делается в конце генерации
RTL. Функция должна удовлетворять некоторым критериям, которые сейчас
касаются ее размера и типов и количества ее параметров. Обратите
внимание, что эта функция может содержать циклы, рекурсивные обращения
к себе (функции с "хвостовой" рекурсией могут быть встроенными!),
операторы перехода, короче говоря, все конструкции, поддерживаемые GNU
CC.
Файл " integrate.c " содержит код для сохранения RTL функции для
того, чтобы потом его вставлять, и вставки этого RTL, когда функция
- 12 -
вызывается. Файл заголовка " integrate.h " также используется для этой
цели.
Опция " -dr " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .rtl " к имени входного
файла.
* Оптимизация переходов. Этот проход упрощает переходы к следующей
команде, переходы через переходы и переходы к переходам. Он удаляет
неиспользуемые метки и недостижимый код, за исключением того, что
недостижимый код, который содержит цикл, не распознается как
недостижимый в этот проход. (Такие циклы удаляются позже при анализе
базовых блоков). Он также преобразует некоторый код, первоначально
написанный с переходами, в последовательности команд, которые
непосредственно устанавливают значения по результатам сравнений, если
машина имеет такие команды.
Оптимизация переходов выполняется два или три раза. Первый раз
происходит сразу после генерации RTL. Второй раз - после CSE, но
только если CSE сообщает, что повторная оптимизация перехода
необходима. Последний раз - перед заключительным проходом. На этом
проходе перекрестные переходы и стирание пустых команд перемещения
выполняются вместе с оптимизациями, описанными выше.
Исходный файл этого прохода - " jump.c ".
Опция " -dj " вызывает дамп отладки RTL кода после первого
выполнения этого прохода. Имя файла дампа получается добавлением "
.jump " к имени входного файла.
* Просмотр регистров. Этот проход находит, когда первый и когда
последний раз использовался каждый регистр; это потребуется для
удаления общих подвыражений. Источник находится в " regclass.c ".
* Слияние переходов. Этот проход распознает условные переходы,
ветви которых ведут к идентичному или обратному тесту. Такие переходы
могут быть "слиты" через второй условный тест. Исходный текст этого
прохода находится в " jump.c ". Эта оптимизация выполняется, только
- 13 -
если установлена опция "-fthread-jumps".
* Удаление общих подвыражения (CSE). Этот проход также вычисляет
константные выражения. Исходный файл - " cse.c ". Если после
вычисления константных выражений некоторые условные переходы
становятся безусловным или неисполняемыми, то по окончании CSE снова
выполняется оптимизация переходов.
Опция " -ds " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .cse " к имени входного
файла.
* Оптимизация циклов. Этот проход перемещает постоянные выражения
за пределы циклов и выполняет упрощения тела цикла или
раскрутку цикла. Исходные файлы - " loop.c " и "unroll.c", а также
файл заголовка " loop.h ", используемый для связи между ними.
Раскрутка цикла использует некоторые функции в " integrate.c " и
заголовок " integrate.h ".
Опция " -dL " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .loop " к имени входного
файла.
* Если была установлена опция " -frerun-cse-after-loop ", второй
проход общего удаления подвыражения выполняется после прохода
оптимизации цикла. В этом случае слияние переходов также снова
выполняется в это время.
Опция " -dt " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .cse2 " к имени входного
файла.
* "Глупое" распределение регистров выполняется в этом месте при
трансляции без оптимизации. Этот проход производит небольшой потоковый
анализ. Если используется "глупое" распределение регистров, следующим
выполняется проход перезагрузки; все проходы между ними пропускаются.
Исходный файл - " stupid.c ".
- 14 -
* Потоковый анализ данных (" flow.c "). Этот проход делит
программу на базовые блоки (и по ходу работы удаляет недостижимые
циклы); затем он вычисляет, какие псевдорегистры "живут" в каждой
точке программы и делает первую команду, которая использует значение,
указывающей на команду, которая вычислила значение.
Этот проход также удаляет вычисления, результаты которых никогда
не используются, и объединяет ссылки на память с командами сложения
или вычитания для получения адресации с автоинкрементом и
автодекрементом.
Опция " -df " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .flow " к имени входного
файла. Если используется "глупое" распределение регистров, этот файл
дампа отражает полные результаты такого распределения.
* Комбинирование команд (" combine.c "). Этот проход делает
попытку объединить группы из двух или трех команд, относящихся к
потоку данных, в одиночные команды. Он объединяет RTL выражения,
заменяя их на команды, упрощает результат, использующий алгебру, и
затем пытается согласовать результат с машинным описанием.
Опция " -dc " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .combine " к имени входного
файла.
* Планирование команд (" sched.c "). Этот проход ищет команды,
чей вывод не будет доступен ко времени его использования в последующих
командах. (Команды загрузки памяти и работы с плавающей точкой часто
ведут себя так на RISC машинах). Он переупорядочивает команды внутри
базового блока, чтобы попытаться разделить определение и использование
элементов, которые иначе вызвали бы приостановку работы.
Планирование команд выполняется дважды. Первый раз - сразу после
комбинирования команд и второй - сразу после перезагрузки.
Опция " -dS " вызывает дамп отладки RTL кода после первого
- 15 -
выполнения этого прохода. Имя файла дампа получается добавлением "
.sched " к имени входного файла.
* Выбор класса регистров. RTL код просматривается, чтобы
выяснить, какой класс регистров является самым лучшим для каждого
псевдорегистра. Исходный файл - " regclass.c ".
* Локальное распределение регистров (" local-alloc.c "). Этот
проход распределяет аппаратные регистры псевдорегистрам, которые
используются только внутри одного базового блока. Поскольку базовый
блок линеен, он может использовать быстрые и мощные методы и получать
очень хорошие результаты.
Опция " -dl " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .lreg " к имени входного
файла.
* Глобальное распределение регистров (" global.c "). Этот проход
распределяет аппаратные регистры для оставшихся псевдорегистров (тех,
чьи промежутки жизни не содержатся в одном базовом блоке).
* Перезагрузка. Этот проход перенумеровывает псевдорегистры
номерами аппаратных регистров, в которые они были распределены.
Псевдорегистры, не получившие аппаратных регистров заменяются на слоты
стека. Затем он находит команды, которые являются недопустимыми,
потому что значение не смогло закончиться в регистре или закончилось в
регистре неправильного вида. Он исправляет эти команды, временно
перезагружая эти значения в регистры. Генерируются дополнительные
команды для копирования.
Проход перезагрузки также может устранять указатель кадра и
вставлять команды для сохранения и восстановления регистров,
использующихся вызываемой функцией, до и после вызова.
Исходные файлы - " reload.c " и " reload1.c ", а также заголовок
" reload.h ", используемый для связи между ними.
Опция " -dg " вызывает дамп отладки RTL кода после этого прохода.
- 16 -
Имя файла дампа получается добавлением " .greg " к имени входного
файла.
* Планирование команд повторяется здесь, чтобы попытаться
избежать остановки потока команд из-за загрузок памяти,
сгенерированных для "пролитых" псевдорегистров.
Опция " -dR " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .sched2 " к имени входного
файла.
* Оптимизация переходов повторяется, включая на этот раз
перекрестные переходы и удаление пустых команд перемещения.
Опция " -dJ " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .jump2 " к имени входного
файла.
* Планирование задержанных переходов. Этот необязательный проход
пытается найти команды, которые могут войти в слоты задержки другой
команд, обычно переходов и вызовов функций. Имя исходного файла - "
reorg.c ".
Опция " -dd " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .dbr " к имени входного
файла.
* Преобразование из использования некоторых аппаратных регистров
к использованию регистров стека может быть выполнено в этом месте. В
настоящее время это поддерживается только для регистров сопроцессора
Intel 80387 с плавающей точкой. Имя исходного файла - " reg-stack.c ".
Опция " -dk " вызывает дамп отладки RTL кода после этого прохода.
Имя файла дампа получается добавлением " .stack " к имени входного
файла.
* Заключительный проход. Этот проход выводит код ассемблера для
функции. Он является также ответственным за идентификацию ложных
- 17 -
команд проверки и сравнения. Специфическая для машины оптимизация
выполняется в это же время. Последовательности для входа и выхода из
функции генерируются непосредственно как код ассемблера в этом
проходе; они никогда не существуют как RTL.
Исходные файлы - " final.c " и " insn-output.c "; последний
генерируется автоматически из машинного описания утилитой " genoutput
". Файл заголовка " conditions.h " используется для связи между этими
файлами.
* Вывод информации об отладке. Он выполняется после
заключительного прохода, потому что он должен вывести смещения слотов
стека для псевдорегистров, которые не получили аппаратных регистров.
Исходные файлы - " dbxout.c " для формата таблицы символов DBX, "
sdbout.c " для формата таблицы символов SDB, и " dwarfout.c " для
формата таблицы символов DWARF.
Некоторые дополнительные файлы используются всеми или многими
проходами:
* Каждый проход использует " machmode.def " и " machmode.h ",
которые определяют машинные типы.
* Несколько проходов используют " real.h ", который определяет
представление по умолчанию констант с плавающей точкой и как
производить с ними операции.
* Все проходы, которые работают с RTL, используют файлы заголовка
" rtl.h " и " rtl.def " и подпрограммы в файле " rtl.c ". Утилиты "
gen* " также используют эти файлы, чтобы читать машинное описание RTL
и работать с ним.
* Несколько проходов обращаются к файлу заголовка
"insn-config.h", который содержит несколько параметров
(макроопределений C), генерирующихся автоматически из машинного
описания RTL утилитой " genconfig ".
* Несколько проходов используют распознаватель команд, который
- 18 -
состоит из " recog.c " и " recog.h ", а также файлы " insn-recog.c " и
" insn-extract.c ", которые генерируются автоматически из машинного
описания утилитами " genrecog " и " genextract ".
* Несколько проходов используют файлы заголовка " regs.h ",
который определяет информацию, записанную об использовании
псевдорегистров, и " basis-block.h ", который определяет информацию,
записанную о базовых блоках.
* " hard-reg-set.h " определяет тип " HARD_REG_SET ", битовый
вектор с битом для каждого аппаратного регистра, и некоторые
макрокоманды для управления им. Этот тип - " int ", если машина имеет
достаточно мало регистров; иначе это - массив " int ", и некоторые
макрокоманды расширяются до циклов.
* Несколько проходов используют атрибуты команд. Определение
атрибутов, определенных для конкретной машины, находятся в файле "
insn-attr.h ", который генерируется из машинного описания программой "
genattr ". Файл " insn-attrtab.c " содержит подпрограммы для получения
значений атрибутов для insns. Оно генерируется из машинного описания
программой " genattrtab ".
.- 19 -
4. Представление RTL
Большинство работы транслятора выполняется в промежуточном
представлении, называемом register transfer language (RTL) .На этом
языке команды, которые нужно выводить, описаны довольно подробно одна
за другой в алгебраической форме, которая описывает то, что делает
команда.
RTL построен на идеях списков Lisp. Он имеет как внутреннюю
форму, состоящую из структур, которые указывают на другие структуры,
так и текстовую форму, которая используется в машинном описании и в
напечатанных дампах отладки. Текстовая форма использует вложенные
круглые скобки, чтобы определить указатели во внутренней форме.
4.1. Типы объектов RTL
RTL использует пять видов объектов: выражения, целые числа,
широкие целые числа, строки и векторы. Выражения - наиболее важные.
Выражение RTL ("RTX", для краткости) - структура C, но оно обычно
всречается как указатель; тип с именем " rtx ".
Целое число - просто " int "; его форма записи использует
десятичные цифры. Широкое целое число - интегральный объект типа"
HOST_WIDE_INT " (* См.: Конфигурация::.); его форма записи использует
десятичные цифры.
Строка - последовательность символов. В ядре она представляется
как " char * " обычным способом C, и синтаксис ее записи такой же, как
в C. Однако, строки в RTL никогда не могут быть пустыми. Если Вы
пишете пустую строку в машинном описании, она представляется в ядре
скорее как пустой указатель, чем как указатель на нулевой символ . В
определенном контексте эти пустые указатели могут употребляться вместо
строк. Внутри кода RTL строки чаще всего находятся внутри выражений `
symbol_ref ', но они появляются и в других контекстах в выражениях
RTL, которые создают машинные описания.
- 20 -
Вектор содержит произвольное число указателей на выражения. Число
элементов в векторе явно указано в векторе. Форма записи вектора
состоит из квадратных скобок (" [...] "), в которые заключаются
элементы, записанные последовательно с пропусками, разделяющими их.
Векторы длины ноль не создаются; вместо них используются пустые
указатели.
Выражения классифицируются по "кодам выражения" (также называемым
RTX кодами). Код выражения - имя, определенное в " rtl.def ", которое
является также (в верхнем регистре) константой перечисления C.
Возможные коды выражений и их значения машинно-независимы. Код RTX
может быть получен с помощью макрокоманды " GET_CODE (X) " и изменен с
помощью " PUT_CODE (X, NEWCODE) ".
Код выражения определяет, сколько операндов содержит выражение и
какого они вида объектами являются. В RTL, в отличие от Lisp, Вы не
можете сказать, глядя на операнд, какого это вида объект. Поэтому Вы
должны узнавать это из контекста - из кода выражения выражения,
содержащего объект. Например, в выражении с кодом " subreg " первый
операнд должен быть выражением, а второй операнд - целым числом. В
выражении кода " plus " два операнда, и оба они должны быть
выражениями. В выражении " symbol_ref " один операнд, который должен
быть строкой.
Выражения записываются как круглые скобки, содержащие имя типа
выражения, его флаги и машинный тип, если они есть, а затем операнды
выражения (разделяемые пробелами).
Имена кода выражения в файле " md " записаны строчными буквами,
но когда они появляются в коде C, они записываются в верхнем регистре.
В этом руководстве они пишутся следующим образом: " const_int ".
В нескольких контекстах пустой указатель может использоваться как
выражение. Форма записи этого - " (nil) ".
4.2. Доступ к Операндам
- 21 -
Для каждого типа выражения " rtl.def " указывает количество
содержащихся объектов и их виды следующим способом: " e " для
выражения (фактически указателя на выражение), " i " для целого числа,
" w " для широкого целого числа, " s " для строки и " E " для вектора
выражений. Последовательность символов для кода выражения называется
его форматом. Таким образом, форматом " subreg " является " ei ".
Иногда используются несколько других символов управления форматом:
" u "
" u " эквивалентно " e " за исключением того, что оно печатается
по-другому в отладочных дампах. Это используется для указателей
на insns.
" n "
" n " эквивалентно " i " за исключением того, что оно печатается
по-другому в отладочных дампах. Это используется для номера
строки или количества кода insn " note ".
" S "
" S " обозначает строку, которая является необязательной. В
объектах RTL в ядре " S " эквивалентно " s ", но когда объект
читается из " md " файла, строковое значение этого операнда может
быть опущено. Опущенная строка считается пустой.
" V "
" V " обозначает вектор, который является необязательным. В
объектах RTL в ядре " V " эквивалентно " E ", но когда объект
читается из " md " файла, векторное значение этого операнда может
быть опущено. Опущенный вектор - то же, что и вектор, не имеющий
элементов.
" 0 "
" 0 " означает слот, чей содержание не попадает ни в какую
нормальную категорию. " 0 " слоты не печатаются вообще в дампах,
и часто используются специальными способами в маленьких частях
транслятора.
- 22 -
Имеются макрокоманды для получения числа операндов, формата и
класса кода выражения:
" GET_RTX_LENGTH (CODE) "
Число операндов RTX кода CODE.
" GET_RTX_FORMAT (CODE) "
Формат RTX кода CODE как строка C.
" GET_RTX_CLASS (CODE) "
Одиночный символ, представляющий тип операции RTX, которую
выполняет код CODE.
Определены следующие классы:
" o "
RTX код, который представляет фактический объект, типа " reg " или
" mem ". " subreg " не принадлежит к этому классу.
" < "
RTX код для сравнения. Коды в этом классе
" NE ", " EQ ", " LE ", " LT ", " GE ", " GT ", " LEU ", " LTU ",
" GEU ", " GTU ".
" 1 "
RTX код для унарной арифметической операции, типа " neg ".
" c "
RTX код для коммутативной бинарной операции, отличной от
" NE " и " EQ " (которые имеют класс " < ").
" 2 "
RTX код для некоммутативной двоичной операции, типа
" MINUS ".
" b "
RTX код для операции с битовым полем, " ZERO_EXTRACT " или
" SIGN_EXTRACT ".
- 23 -
" 3 "
RTX код для трех других входных операций, типа
" IF_THEN_ELSE ".
" i "
RTX код для машинного insn (" INSN ", " JUMP_INSN " и
" CALL_INSN ").
" m "
RTX код для того,что соответствует insns, типа
" MATCH_DUP ".
" x "
Все другие RTX коды.
К операндам выражений обращаются, используя макрокоманды " XEXP
", " XINT ", " XWINT " и " XSTR ". Каждая из этих макрокоманд имеет
два параметра: указатель на выражение (RTX) и номер операнда
(отсчитываемый от нуля). Таким образом,
XEXP (X, 2)
обращается к операнду 2 выражения X как к выражению.
XINT (X, 2)
обращается к тому же самому операнду как к целому числу. Макрокоманда" XSTR ",
использованная тем же самым способом, получила бы этот операнд как строку.
К любому операнду можно обращаться как к целому числу, выражению
или строке. Вы должны выбрать правильный метод доступа для вида
значения, фактически содержащегося в операнде. Это делается при помощи
кода выражения, содержащего операнд. Таким же образом Вы можете
узнать, сколько это выражение имеет операндов.
Например, если X - выражение " subreg ", Вы знаете, что оно имеет
два операнда, к которым можно корректно обращаться как " XEXP (X, 0) "
- 24 -
и " XINT (X,1) ". Если Вы напишете " XINT (X, 0) ", Вы получите адрес
операнд выражения как целое число; это может быть иногда полезно, но
чтобы получить такой результат, лучше писать " (int) XEXP (X, 0) ". "
XEXP( X, 1) " тоже скомпилируется без ошибки и возвратит второй,
целочисленный операнд,приведенный к указателю на выражение, обращение
к которому, скорее всего, приведет к аварийному отказу. Ничто не
запрещает Вам писать " XEXP( X, 28) ", но эта запись обратится к
памяти после конца выражения с непредсказуемыми результатами.
Доступ к операндам, которые являются векторами, сложнее. Вы
можете использовать макрокоманду " XVEC ", чтобы получить указатель на
вектор непосредственно, или макрокоманды " XVECEXP " и " XVECLEN ",
чтобы обратиться к элементам и длине вектора.
" XVEC (EXP, IDX) "
Получает указатель на вектор, который является операндом номер IDX в
выражении EXP.
" XVECLEN (EXP, IDX) "
Получает длину (число элементов) в векторе, который является
операндом номер IDX в выражении EXP. Это значение типа " int ".
" XVECEXP (EXP, IDX, ELTNUM) "
Обращается к элементу номер ELTNUM в векторе, который является
операндом номер IDX в выражении EXP. Это значение RTX.
При вызове этой макрокоманды нужно быть уверенным, что ELTNUM не
меньше нуля и меньше чем " XVECLEN (EXP, IDX) ".
Все макрокоманды, определенные в этом разделе, расширяются в
именующие выражения и, следовательно, могут использоваться для
присваивания значения операндам, длинам и элементам векторов так же,
как и для обращения к ним.
4.3. Флаги в выражении RTL
Выражения RTL содержат несколько флагов (битовых полей с одним
- 25 -
битом), которые используются в некоторых типах выражений. Наиболее
часто к ним обращаются следующими макрокомандами:
" MEM_VOLATILE_P (X) "
В выражениях " mem " отлично от нуля для переменных ссылок памяти.
Сохраняется в поле " volatil " и печатается как " /v ".
" MEM_IN_STRUCT_P (X) "
В выражениях " mem " отлично от нуля для ссылки на всю
структуру, объединение или массив, или на их компонент. Ноль для
ссылки на скалярную переменную или через указатель на скаляр.
Сохраняется в поле " in_struct " и печатается как " /s ".
" REG_LOOP_TEST_P "
В выражениях " reg " отлично от нуля, если этот регистр
"живет" только в коде проверки на выход некоторого цикла.
Сохраняется в поле " in_struct " и печатается как " /s ".
" REG_USERVAR_P (X) "
В " reg " отлично от нуля, если соответствует переменной, имеющейся в
исходном тексте пользователя. Ноль для временных переменных, внутренне
сгенерированных транслятором. Сохраняется в поле " volatil " и печатается
как " /v ".
" REG_FUNCTION_VALUE_P (X) "
Отлично от нуля в " reg ", если это - место, в которое должно быть
возвращено значение функции. (Это возможно только для аппаратного
регистра.) Сохраняется в поле " integrated " и печатается как " /i ".
Тот же самый аппаратный регистр может использоваться также для значений
функций, вызываемых этой, но " REG_FUNCTION_VALUE_P " - ноль
для этого вида использования.
" SUBREG_PROMOTED_VAR_P "
Отлично от нуля в " subreg ", если делается во время доступа к объекту,
продвинутому на более широкий тип в соответствии с макрокомандой
машинного описания " PROMOTED_MODE " (* См.: Размещение памяти::.). В
этом случае тип " subreg " является объявленным типом объекта
- 26 -
и тип " SUBREG_REG " является типом регистра, который содержит объект.
Расширяемые ые переменные всегда расширяются нулем или знаком в
более широкий тип при каждом присваивании.
Сохраняется в поле " in_struct " поле и печатается как " /s ".
" SUBREG_PROMOTED_UNSIGNED_P "
Отлично от нуля в " subreg " который имеет " SUBREG_PROMOTED_VAR_P "
отличным от нуля, если вызываемый объект расширен нулем, и ноль, если он
расширен знаком. Сохраняется в поле " unchanging " и
печатается как " /u ".
" RTX_UNCHANGING_P (X) "
Отлично от нуля в " reg " или " mem ", если значение не изменено. (Этот
флаг не устанавливается для ссылок на константы через указатели.
Такие указатели гарантируют только, что объект будет не изменен
явно текущей функцией. Объект может бы быть изменен
другими функциями или через совмещение имен.) Сохраняется в поле
" unchanging " и печатается как " /u ".
" RTX_INTEGRATED_P (INSN) "
Отлично от нуля в insn, если получено из вызова встроенной функции.
Сохраняется в поле " integrated " и печатается как " /i ". Может
быть удалено; в настоящее время от него ничего не зависит.
" SYMBOL_REF_USED (X) "
В " symbol_ref " указывает, что X использован.
Обычно используется только для того, чтобы гарантировать, что X
только однажды объявлен внешним. Сохраняется в поле " used ".
" SYMBOL_REF_FLAG (X) "
В " symbol_ref " используется как флаг для специфических для машины
целей. Сохраняется в поле " volatil " и печатается как " /v ".
" LABEL_OUTSIDE_LOOP_P "
В выражениях ` label_ref ' отлично от нуля, если это - ссылка на
метку, которая находится снаружи самого внутреннего цикла, содержащего
ссылку на метку. Сохраняется в поле " in_struct " и печатается как " /s ".
- 27 -
" INSN_DELETED_P (INSN) "
В insn отлично от нуля, если insn был удален. Сохраняется в
поле " volatil " и печатается как " /v ".
" INSN_ANNULLED_BRANCH_P (INSN) "
В " insn " в слоте задержки ветви insn указывает, что
должна использоваться аннулирующая ветвь. См. обсуждение
" sequence " ниже. Сохраняется в поле " unchanging " и печатается
как " /u ".
" INSN_FROM_TARGET_P (INSN) "
В " insn " в слоте задержки ветви указывает, что insn -
из адреса перехода. Если для ветви insn
" INSN_ANNULLED_BRANCH_P " установлен, этот insn должен выполняться,
только если переход производится. Для аннулированных ветвей с этим битом, равным 0,
insn должен выполняться, если переход не производится.
Сохраняется в поле " in_struct " и печатается как " /s ".
" CONSTANT_POOL_ADDRESS_P (X) "
Отлично от нуля в " symbol_ref " если он ссылается на часть текущего
"пула констант" функции. Это адреса, близкие к
началу функции, и GNU CC предполагает, что они могут быть адресованы
непосредственно (возможно, с помощью базовых регистров). Сохраняется в
поле " unchanging " и печатается как " /u ".
" CONST_CALL_P (X) "
В " call_insn " указывает, что insn представляет обращение к
постоянной функции. Сохраняется в поле " unchanging " и печатается
как " /u ".
" LABEL_PRESERVE_P (X) "
В " code_label " указывает, что метка никогда не может быть удалена.
Метки, вызываемые нелокальным переходом, будут иметь этот набор битов.
Сохраняется в поле " in_struct " и печатается как " /s ".
" SCHED_GROUP_P (INSN) "
В течение планирования команд в insn указывает, что
предыдущий insn должен планироваться вместе с этим insn. Это
- 28 -
используется, чтобы гарантировать, что определенные группы команд не
будут разбиваться на части проходом планирования команд, например,
" use " insns перед " call_insn " не могут отделяться от
" call_insn ". Сохраняется в поле " in_struct " и печатается как " /s ".
Вот поля, к которым обращаются вышеупомянутые макрокоманды:
" used "
Обычно этот флаг используется только в один момент, а именно - в конце
генерации RTL функции, чтобы посчитать, сколько раз
в insns появляется выражение. Выражения, которые появляются больше чем
однажды, копируются, согласно правилам совместного использования
структур (* См.: Совместное использование::.).
В " symbol_ref " он указывает что внешнее объявление для
символа уже было написано.
В " reg " он используется регистром листа, перенумеровывающим код,
чтобы гарантировать, что каждый регистр перенумеровывается только
однажды.
" volatil "
Этот флаг используется в выражениях " mem ", " symbol_ref " и " reg "
и в insns. В файлах дампа RTL он печатается как " /v ".
В выражении " mem " он равен 1, если ссылка памяти переменная.
Переменные ссылки памяти нельзя удалять, переупорядочивать и объединять.
В выражении ` symbol_ref ', это используется для специфических для
машины целей.
В выражении ` reg ' он равен 1, если значение - пользовательская
переменная, и 0, если это внутренняя временная переменная транслятора.
В insn, значение 1 означает, что insn был удален.
" in_struct "
В выражениях " mem " он равен 1, если данная величина ссылается на
- 29 -
весь массив или структуру или их часть ; 0, если это - скалярная
переменная (или может быть ею). Ссылке через указатель C соответствует 0,
потому что указатель может указывать на скалярную переменную.
Эта информация позволяет транслятору выяснить определенную информацию
относительно возможных случаев совмещения имен.
В insn в слоте задержки перехода 1 означает, что этот insn -
из адреса перехода.
Во время планирования команд в insn 1 означает, что этот insn
должен планироваться как часть группы вместе с предыдущим
insn.
В выражениях " reg " он равен 1, если регистр
"живет" только в коде проверки на выход некоторого цикла.
В выражениях " subreg " 1 означает, что " subreg " обращается
к объекту, который имел тип, полученный из более широкого типа.
В выражениях " label_ref " 1 означает, что ссылка на
метку находится снаружи самого внутреннего цикла, содержащего insn, в
котором было найдено " label_ref "
В выражениях ` code_label ' он равен 1, если метку никогда нельзя
удалять. Это используется для меток, на которые имеются
нелокальные переходы.
В дампе RTL этот флаг представляется как " /s ".
" unchanging "
В выражениях " reg " и " mem " 1 означает, что значение
выражения никогда не изменяется.
В выражениях " subreg " он равен 1, если " subreg " ссылается на
объект без знака, чей тип был расширен до более широкого тип.
В insn 1 означает, что это - аннулирующая ветвь.
- 30 -
В выражении " symbol_ref ", 1 означает, что этот символ адресует
что-то в пуле констант функции.
В " call_insn " 1 означает, что эта команда - обращение к
постояннной функции.
В дампе RTL этот флаг представляется как " /u ".
" integrated "
В некоторых видах выражений, включая insns, этот флаг означает, что
RTL был произведен интеграцией процедуры.
В выражении " reg " этот флаг указывает регистр, содержащий
значение, которое будет возвращено текущей функцией. На машинах
которые передают параметры в регистрах, то же количество регистров
может использоваться и для параметров, но этот флаг не устанавливается
при таком использовании.
4.4. Машинные типы
Тип описывает размер данных объекта и представление его использования.
В С-коде машинные рехимы (типы) представляются перечислимым типом
`enum machine_mode', определенным в `machmode.def'. В каждом RTL выражение
имеется место для типа. Также делается в некоторых типах tree-выражений
(точнее в описанияхи типах).
В отладочных дампах и описаниях машинный тип RTL-выражения записан после кода
выражения через двоеточие. Символ 'mode', который должен стоять в конце
названия каждого типа опущен. Например, `(reg:SI 38)' - обозначает
выражение 'reg' типа SImode. Если тип `VOIDmode' то это не пишется вообще.
Здесь представленна таблица типов. Далее "byte" - это объект
размером `BITS_PER_UNIT' бит.
`QImode'
"Quarter-Integer" - тип, в котором байт рассматривается как целое.
- 31 -
`HImode'
"Half-Integer" - тип, представляющий целое двумя байтами.
`PSImode'
"Partial Single Integer" - тип, представляющий целое, занимающее
четыре байта, но все четыре реально не используются. На некоторых
машинах это правильный режим для представления указателей.
`SImode'
"Single Integer" - тип, представляющий целое четырьмя байтами.
`PDImode'
"Partial Double Integer"- тип, представляющий целое, занимающее
восемь байт, но все четыре реально не используются. На некоторых
машинах правильно представлять указатель этим типом.
`DImode'
"Double Integer" - тип, представляющий целое восьмью байтами.
`TImode'
"Tetra Integer" (?) - тип, представляющий целое шестнадцатью байтами.
`SFmode'
"Single Floating" - тип числа с плавающей точкой, имеющего
одинарную точность (четыре байта).
`DFmode'
"Double Floating" - тип числа с плавающей точкой, имеющего
двойную точность (восемь байт).
`XFmode'
"Extended Floating" - тип числа с плавающей точкой, имеющег
тройную точность (двенадцать байта). Этот тип используется для
хранения чисел с плавающей точкой в формате IEEE. В некоторых системах
не все биты реально используются.
`TFmode'
"Tetra Floating"- тип числа с плавающей точкой, имеющего
- 32 -
четверную точность (шестнадцать байт).
`CCmode'
"Condition Code" - тип представляющий значение операций сравнения.
Результат сравнения представляется, как набор бит на каждой машине по
своему. Этот тип не используется на машинах, использующих `cc0'.
`BLKmode'
"Block" - тип, представляющий значение которое нельзя представить
никаким другим типом. В RTL этот тип используется только для ссылок на
память, и только в том сслучае если они используются в командах
относяшихся к строкам и векторам. В машинах, не имеющих команд такого
типа этот тип вообще не появляется.
`VOIDmode'
Тип Void значит либо отсутствие типа либо неопределенный тип. Например,
RTL-выражение `const_int' имеет тип VOIDmode, потому что его тип зависит
от контекста. В отладочной информации RTL выражения типа `VOIDmode'
обозначаются отсутствием какого бы то ни было типа.
`SCmode, DCmode, XCmode, TCmode'
Этот тип комплексных чисел, представляемый парой чисел с плавающей
точкой. Значение числа с плавающей точкой будет соответственно типа
'SFmode', `DFmode', `XFmode', или `TFmode'.
`CQImode, CHImode, CSImode, CDImode, CTImode, COImode'
Этот тип комплексных чисел, представляемый парой целых чисел.
Значение целого числа будет соответственно типа `QImode', `HImode',
`SImode', `DImode', `TImode', или `OImode'.
C-макрос `Pmode' описывает тип, который используется для адреса.
Обычно этот тип имеет размер `BITS_PER_WORD' бит. На 32-битовых машинах это `SImode'.
Единственный тип, который обязан поддерживаться машиной - `QImode' и
типы соответствующие размерам `BITS_PER_WORD', `FLOAT_TYPE_SIZE' и
`DOUBLE_TYPE_SIZE'. По умодчанию, транслятор быдет пытаться использовать
для 8-байтных структур тип "DImode", но это может быть предотвращено с помошью
отмены области определения `MAX_FIXED_MODE_SIZE'. Аналогично для 16-битных
- 33 -
структур транслятор использует `TImode', и аналогично Вы можете принимать
меры для указания С-типа 'short int', что бы избежать использования 'HImode'.
В компиляторе остается очень мало точных указаний типов, но скоро не
останется даже этого. Вместо этого типы делятся на классы. Классы представляются
перечислением `enum mode_class', определенным в `machmode.h'. Ниже приведены
возможные классы:
`MODE_INT'
Целые типы. По умолчанию это: `QImode', `HImode', `SImode',`DImode', и
`TImode'.
`MODE_PARTIAL_INT'
Частичные целые (не все бтиы используются): `PSImode' and `PDImode'.
`MODE_FLOAT'
Типы чисел с плавающей точкой. По умолчанию это: `SFmode', `DFmode',
`XFmode' и `TFmode'.
`MODE_COMPLEX_INT'
Комплексные целые. (Пока еще не реализованы).
`MODE_COMPLEX_FLOAT'
Комплексные числа (с плавающей точкой). По умолчанию это: `SCmode',
`DCmode', `XCmode', и `TCmode'.
`MODE_FUNCTION'
Паскалевские и алголовские функцмональные переменные, включая
статические цепочки. (Пока еще не реализованы).
`MODE_CC'
Типы соответствующие результатам сравнений. Это `CCmode' и все типы,
перечисленные в макросе `EXTRA_CC_MODES'. см. Образцы переходов,
см. также *Note Код условия
`MODE_RANDOM'
Этот класс состовляют типы, не попавшие ни в один из вышеперечисленных
классов. Пока что это типы `VOIDmode' и 'BLKmode'.
- 34 -
Далее идут некоторые макросы, относящиеся к типам:
`GET_MODE (X)'
Возвращает тип переменной X.
`PUT_MODE (X, NEWMODE)'
Меняет тип переменной X на NEWMODE.
`NUM_MACHINE_MODES'
Возвращает количество типов, доступных на машине, под которую происходит
компиляция. Это число на единицу больше максимального номера доступного
типа.
`GET_MODE_NAME (M)'
Возвращает название типа M в виде строки.
`GET_MODE_CLASS (M)'
Возвращает класс типа M.
`GET_MODE_WIDER_MODE (M)'
Возвращает следующий по размеру тип. Например, выражение
`GET_MODE_WIDER_MODE (QImode)' возвращает `HImode'.
`GET_MODE_SIZE (M)'
Возвращает количество байт, которое занимает объект типа M.
`GET_MODE_BITSIZE (M)'
Возвращает количество бит, которое занимает объект типа M.
`GET_MODE_MASK (M)'
Возвращает битовую маску, содержащую 1 в тех битах, которые используются
типом M. Этот макрос может быть использован только для типов чей размер
не больше `HOST_BITS_PER_INT' бит.
`GET_MODE_ALIGNMENT (M))'
Возвращает количество бит, необходимое для выравнивания объекта типа M.
- 35 -
`GET_MODE_UNIT_SIZE (M)'
Возвращает размер в байтах одного поля объекта типа M. Это тоже самое,
что и `GET_MODE_SIZE' для всех некомплексных типов, для комплексных это
размер вещественной или мнимой части.
`GET_MODE_NUNITS (M)'
Возвращает количество полей в типе M. Тоже самое что
`GET_MODE_SIZE' поделенное на `GET_MODE_UNIT_SIZE'.
`GET_CLASS_NARROWEST_MODE (C)'
Возвращает narrowest тип в классе C.
Глобальные переменные `byte_mode' и 'word_mode' содержат типы,
принадлежащие классу `MODE_INT', чьи битове размеры равны соответственно
`BITS_PER_UNIT' и 'BITS_PER_WORD'. На 32-битных машинах это `QImode' и
`SImode'.
4.5. Типы константных выражений
Простейшее RTL-выражение - это выражение, описывающее константную
величину.
`(const_int I)'
Выражение такого типа представляет целую константу со значением I.
К I обычно обращаются с помощью макроса `INTVAL' как в `INTVAL (EXP)',
который эквивалентет `XWINT (EXP, 0)'.
Существует только один константный объект соответствующий целому
значению ноль. Это значение переменной `const0_rtx'. Аналогично,
целое значение 'единица' расположенна в `const1_rtx', а двойка в
`const2_rtx'. Так же значение целой минус единицы расположенно в
`constm1_rtx'. При создании целой константы со значением
ноль, единица, двойка или минус единица будет соответственно
возвращена константа `const0_rtx', `const1_rtx', `const2_rtx' или
`constm1_rtx'.
Аналогично существует только одна целая константа со значением
`STORE_FLAG_VALUE' - это `const_true_rtx'. Если `STORE_FLAG_VALUE'
- 36 -
равен единице то `const_true_rtx' и `const1_rtx' будут указывать на
одини тот же объект. Если `STORE_FLAG_VALUE' равен -1, то на один и тот
же объект будут указывать `const_true_rtx' и `constm1_rtx'.
`(const_double:M ADDR I0 I1 ...)'
Может описывать как константу с плавающей точкой, так и целую константу
слишком большую, что бы поместится в `HOST_BITS_PER_WIDE_INT' битах но
достаточно маленькую, что бы поместится в 2*`HOST_BITS_PER_WIDE_INT'
битах(GNU CC не предоставляет механизма для описания констант большего
размера). В последнем случае константа будет иметь тип 'VOIDmode'.
ADDR используется, чтобы содержать выражение 'mem', которое указывает
на расположение константы в памяти. Если память не была выделена память,
но находится в цепочке всех выражений `const_double' в данной компиляции
(поддерживающей использование неотображаемых полей), ADDR указывает на
`const0_rtx'. Если же находится не в цепочке, ADDR указывает на
`cc0_rtx'. К ADDR обычно обращаются через макрос `CONST_DOUBLE_MEM' а
к полю цепи через `CONST_DOUBLE_CHAIN'.
Если M - "VOIDmode", то биты значения сохранены в I0 и I1. К I0
обычно обращаются с помощью макрокоманды "CONST_DOUBLE_LOW" а к I1 с
помошью "CONST_DOUBLE_HIGH".
Если константа - константа с плавающей точкой (независимо от точности),
то число целых чисел, исспользуемых для сохранения значения зависит от
размера `REAL_VALUE_TYPE'. Целые числа представляют число с плавающей
точкой, но не точно в формате целевой или главной машины. Чтобы
преобразовать их (целые числа) в формат, используемый
на целевой машине, используется макрос `REAL_VALUE_TO_TARGET_DOUBLE' и
иже с ним (см.Data Output::. ).
Макрос `CONST0_RTX (MODE)' указывает на выражение типа MODE со значением
ноль. Если тип MODE из класса `MODE_INT' то макрос будет указывать на
`const0_rtx'. В другом случае он возращает выражение `CONST_DOUBLE'
с типом MODE. Аналогично макрос `CONST1_RTX (MODE)'
возвращает выражение со значением 1 типа MODE, тоже самое и для
`CONST2_RTX'.
- 37 -
`(const_string STR)'
Описывает константную строку со значением STR. В настоящее время
это используется только атрибутов insn (см. Атрибуты Insn.)
с тех пор, как в С строчные константы размещаютсяс в памяти.
`(symbol_ref:MODE SYMBOL)'
Описывает аасемблерную метку для данных. SYMBOL - это строка, содержащая
название метки. Если она начинается с '*' то этот символ в название метки
не включается. В противном случае к названию метки вначале добавляется
символ `_'.
`symbol_ref' содержит тип, который обычно `Pmode'. Обычно это
единственный тип в котором описание строки является коректным.
`(label_ref LABEL)'
Описывает ассемблерную метку для кода. Она содержит один
операнд - выражение, которое должно быть `code_label', которое
появляется, в последовательности команд, идентифицируя место, куда
указывает метка.
Особые метки для кода используются для того, чтобы их можно было бы
отличить в момент оптимизации переходов.
`(const:M EXP)'
Описывает константу, которая является результатом выражения, значение
которого может быть найдено во время компиляционных вычисленний.
Операнд EXP - это выражение которое содержит только константы
(выражения типа 'const_int', `symbol_ref' и `label_ref')
и операции `plus' и `minus'. Не все комбинации являются
допустимыми, т.к. ассемблер не может выполнять произвольные
арифметические выражения над перемещаемыми символами.
M должно быть типа `Pmode'.
`(high:M EXP)'
Описывает старшие биты EXP, обычно - `symbol_ref'. Количество бит
является машинно-зависимым и обычно оно определено
в команде, которая инициализирует количество старших битов регистра.
- 38 -
Эта команда используется с `lo_sum' для представления типичных
последовательностей с двумя командами, используемых в RISC машинах,
для согласования расположения в глобальной памяим.
M должно быть типа `Pmode'.
4.6. Регистры и память
Ниже перечислены типы выражений для доступа к машинным регистрам и
основной памяти.
`(reg:M N)'
Для небольших значений целого числа N (меньше, чем
`FIRST_PSEUDO_REGISTER') это будет ссылка на машинный регистр с номером
N - "аппаратный регист". Для больших значений числа N, возвращается
ссылка на временное значение или "псевдо регистр". Принцип действия
компилятора - создавать код, предполагая неограниченное число таких
псевдо регистров, а поже заменять их на аппаратные регистры или на
ячейки памяти.
M - тип ссылки. Это необходимо, т.к. обычно машина может использовать
каждый регистр более чем в одном режиме. Например могут существовать
инструкции, которые используют регистр, содержащий слово, как полслова
или как байт, более того, могут быть инструкции, ссылающиеся на этот
регистр как на число с плавающей точкой различной точности.
Тип необходимо указывать даже для регистра, который машина может
использовать только одним способом.
Символ `FIRST_PSEUDO_REGISTER' определяется в соответствии с
описанием машины, т.к. так как число аппаратных регистров на машине
- инвариантная характеристика машины. Однако, обратите внимание,
что не все машинные регистры обязаны быть регистрами общего назначения.
Все машинные регистры, которые можно использовать для хранения данных
учтены в числе аппаратных регистров, даже те, которые могут
использоваться только в некоторых коммандах или содержать только
орпеделенные типы данных.
All
- 39 -
К аппаратным регистром можно обращатся в различных режимах в течении одной
функции, но к псевдо регистру можно обращаться только в режиме, в
котором он был описан. Если необходино описать доступ к псевдо регистру
в другом режиме используйте выражение `subreg'.
Выражение 'reg' типа M, которое содржит более одного слова данных,
может на самом деле состоять из нескольких последовательных регистров.
Если кроме того номер регистра соответствует аппаратному регистру,
то фактически он представляется несколькими последовательными
аппаратными регистрами, начиная с данного.
Каждый номер псевдо регистра, используеммого коде RTL-функции
представляется уникальным выражением `reg'.
Некоторые номера псевдо регистров, от `FIRST_VIRTUAL_REGISTER' до
`LAST_VIRTUAL_REGISTER' появляются только в фазе RTL и уничтожаются
перед фазой оптимизации. Пока не завершено RTL образование функций,
они представляют расположение в стеке, которое не может быть определено
пока не завершится RTL-генерация функций.
Определены следующие номера для регистров:
`VIRTUAL_INCOMING_ARGS_REGNUM'
Указывает на первое слово аргументов, переданных в стек.
Обычно это аргументы, переданные вызывающей программой, но
вызывающая программа может положить в стек некоторые аргументы,
которые до этого были в регистрах.
Когда RTL-фаза завершена, этот виртуальный регистр замещается суммой
регистра, полученного с помощью `ARG_POINTER_REGNUM' и значения
'FIRST_PARM_OFFSET'.
`VIRTUAL_STACK_VARS_REGNUM'
Если `FRAME_GROWS_DOWNWARD' определен, то это указывает на место
сразу после первой переменной в стеке. Иначе, это указывает на
первую переменную в стеке.
- 40 -
`VIRTUAL_STACK_VARS_REGNUM' заменен на сумму регистра, полученного
с помощю `FRAME_POINTER_REGNUM' и значения 'STARTING_FRAME_OFFSET'.
`VIRTUAL_STACK_DYNAMIC_REGNUM'
Указывает на расположение динамически распределенной памяти в стеке
сразу после того, как указатель стека инициализирован достаточным
объемом памяти.
Этот виртуальный регистр замещается суммой регистра, полученного с
помощью `STACK_POINTER_REGNUM' и значения `STACK_DYNAMIC_OFFSET'.
`VIRTUAL_OUTGOING_ARGS_REGNUM'
Указывет на место в стеке, куда пишутся выходящие параметры, когда
стек - pre-pushed (помещенные параметры, использующие push insn
всегда используют `STACK_POINTER_REGNUM')
Этот виртуальный регист замещается на сумму регистра, полученного
с помощью `STACK_POINTER_REGNUM' и значения `STACK_POINTER_OFFSET'.
`(subreg:M REG WORDNUM)'
выражение `subreg' используется для обращения к машинному регистру в
режеме, отличном от естственного, или что бы обратится к мульти-регистру
'reg', который фактически является несколькими регистрами.
Каждый псевдо регистр имеет свой естественный режим. Если необходимо
работать с этим регистром в другом режиме - например, выполнить команду
перемещения слова над регистром, который содержит один байт, псевдо
регистр должен быть включен в 'subreg'. В этом случае WORDNUM - ноль.
Обычно размер M по крайней мере не больше размера типа REG,
тогда это ограничивает рассмотрение только тех бит REG, которые
находятся в M.
Иногда разме M больше чем размер типа REG. Тогда эти выражения `subreg'
часто называются "парадоксальными". Они используются в случаях, когда
- 41 -
мы хотим обратиться к объекту в режиме с большим размером, но не
заботиться, какое значение имеют дополнительные биты.
Предварительный проход гарантирует, что "парадоксальные" ссылки сделаны
только на аппаратные регистры.
Еще 'subreg' используется для извлечения одиночного регистра из
значения мулти-регистра. Такие типы как `DImode' и `TImode' могут
содержать число большее чем слово - число которое обычно размещается
в двух или более регистрах. Чтобы обратится к одному из регистров
используйте 'subreg' с режимом 'SImode' и WORDNUM, который показывает
какой нужен регистр.
Записывание в не-парадоксальный 'subreg' дает неопределенный результат
для битов тогоже слова, что и 'subreg'. Эта неточность делает генерацию
кода для таких инструкций более эфективной. Для представления команды,
которая сохраняет все биты 'subreg' используйте `strict_low_part'
around the `subreg'.
Если параметр трансляции `WORDS_BIG_ENDIAN' установлен в 1, то это
означает, что слово номер ноль наиболее значительная(significant) часть,
иначе, что наименее значительная часть
Возможно между проходом комбинирования и проходом перезагрузки, что
будут существовать пародоксальные 'subreg', которые содержат первым
параметром 'mem' вместо 'reg'. После прохода перезагрузки возможно
будут существовать непародоксальные 'subreg', которые содержат 'mem';
обычно это происходит, когда 'mem' является слотом стека, который
заменил псевдорегистр.
Обратите внимание, что некорректно обращаться к числу в режиме
`DFmode', как числу в режиме 'SFmode' через 'subreg'. На некоторых
машинах наиболее значимая часть `DFmode' имеет другой формат, чем
число с плавающей точкой одинарной точности.
Так же некорректно обращаться к одиночному слову аппаратного мульти
регистра, который может содержать более одного слова. Например некоторые
32-битные машины имеют регистры вещественных чисел, которые могут
содержать целое чило типа `DFmode'. Если бы например номер регистра
- 42 -
был бы 10 то `(subreg:SI (reg:DF 10) 1)' было бы некорректным,
т.к. не существует способа представить это указание как отдельный
аппаратный регистр. Пргоход перезагрузки предотвращает формирование
выражений такого типа.
К первому операнду 'subreg' обычно обращаются с помошью макрокоманды
`SUBREG_REG' а ко второму с помошью `SUBREG_WORD'.
`(scratch:M)'
Описывает временный регистр, который требуется для выполнения одиночной
комманды и не используется впоследствии. Он преобразуется в 'reg' либо
локальным регистром allocator либо проходом перезагрузки.
`scratch' обычно описывается внутри операции `clobber'.
`(cc0)'
Обращение к регистру кода условия. Это обращение не имеет параметров
и типа. Существует два способа для его использования:
* Вместо полного набора флагов кодов условий. Это наилучший
способ дл большинства машин, на которых каждое сравнение
устанавливает весь ряд флагов.
С такой техникой '(cc0)' может быть корректно использована
только в двух контекстах: как адрес перехода (в операторах
тестов и сранения) и в операторах сравнения с нулем
(`const_int' с значением нуля, которое определяется,
`const0_rtx').
* Вместо одиночного флага, который является результатом одиглчного
сравнения. Это полезно на машинах, на которых только один флаговый
бит, и на которых инструкции сравнения должны точно определять
проверяемое условие.
С такой техникой '(cc0)' может быть корректно использована
только в двух контекстах: как адрес перехода (в операторах
тестов и сранения) когда источник - оператор сравнения, и как
- 43 -
первый оператор `if_then_else' (в условной ветке).
Имеется только один объект выражения с кодом "cc0"; это - значение
переменной "cc0_rtx". Любая попытка создать выражение с кодом "cc0"
возвратит "cc0_rtx".
Команды могут устанавливать код условия неявно. На многих машинах,
почти все команды устанавливают код условия, в зависимости от значения,
которое они вычислили или записали. Нет необходимости записывать эти
действия в RTL явно, т.к. машинное описание включает предписание для
распознавания таких команд (посредством макрокоманды
"NOTICE_UPDATE_CC"). *см Код Условия. В упоминании нуждаются только
те команды, которые ничего не делают, кроме установки или использования
кода условия.
На некоторых машинах регистр кода условия является регистром с номером,
и тогда вместо '(cc0)' используется`reg'. Обычно это более выгодный
подход, если код условия изменяют тольео небольшое количество команд.
На других машинах код условия сохраняется в общих регмстрах,
в таком случае должны использоваться псевдо регистры.
Некоторые машины, типа Sparc и RS/6000, имеют два набора арифметических
команд, один, который устанавливает, и другой, который не устанавливает
код условия.
Удобнее всего это использовать в случае, когда по умллчанию генерируются
инструкции, не устонавливающие код условия, и есть образцы, которые
и выполняют арифметику и устанавливают регистр кода условия, который
в этом случае не `(cc0)'. Для примеров, поиск "addcc" и
"andcc" в "sparc.md".
`(pc)'
Он представляет машинный счетчик програм. Он не имеет операндов и
может не иметь типа. '(pc)' может быть корректно использован только
в некоторых специфических контекстах операторов перехода.
Существует только один объект, соответствующий коду '(pc)' это
значение переменной `pc_rtx'. Любая попытка создать выражение с кодом
`pc' вернет `pc_rtx'..
- 44 -
Все инструкции, которые не выполняют переход, изменяют счетчик программы
неявно, увеличивая его, но упоминать это в RTL не нужно.
`(mem:M ADDR)'
Это RTX представляет ссылку на основную память по адресу, представляемому
выражением ADDR. M определяет, сколько ячеек памяти доступно.
4.7. RTL-Выражения для работы с арифметикой
Далее если не оговоренно противное, то все операнды в математическом
выражении должны быть применимы к типу M. Операнд применим к типу
M, если он имеет тип M, или это `const_int' или `const_double' а
M - режим класса `MODE_INT'.
В бинарных коммутативных операциях константа должна быть вторым аргументом.
`(plus:M X Y)'
Возвращает сумму значений X и Y выполненыю в режиме M.
`(lo_sum:M X Y)'
Тоже, что и `plus', только складывается X и биты младших разрядов Y.
Количество бит младшего разряда является аппаратно зависимым, но обычно
это количиство бит в типе 'Pmode' минус количество бит установленное с
помошью 'high'. (*см. Constants::.).
M должно быть типа `Pmode'.
`(minus:M X Y)'
Как `plus' только описывает вычитание.
`(compare:M X Y)'
Представляет результат вычитания Y из X с целью сравнения. Результат
вычисляется без переполнения, как если бы был с бесконечной точностью.
Конечно, машина не может вычитать с бесконечной точностью. Однако,
когда нужен только знак, она может симулировать как будто делает это.
И единственный способ, которым это выражение может быть корректно
- 45 -
использоваться - это сохранение его в коде условия.
Тип М не связан с типами X и Y, а является типом значения
кода условия. Если используется `(cc0)', то это `VOIDmode'. Иначе
это какой-то тип класса `MODE_CC', обычно `CCmode'.
см. Код условия.
Обычно X и Y должны иметь один и тот же тип. иначе сравнение корректно
только в случае, когда тип X принадлежит классу `MODE_INT' а Y -
`const_int' или `const_double' с типом `VOIDmode'. Тип X определяет
в какком режиме будет выполнено сравнение, поэтому он не моднт быть
'VOIDMode'.
Если один из операндов - константа, то она должна быть вторым
аргументом и тип сравнения корректируется как соответствующий.
Сравнение двух констант некорректно, так как невозможно узнать режим
в котором проводить сравнение. Сравнение должно также быть свернуто
в течение трансляции, или первый операнд должен быть загружен в регистр,
в то время пока режим еще известен.
`(neg:M X)'
Возврашает X с обратным знаком (вычитание из нуля), операция выполняется
в режиме M.
`(mult:M X Y)'
Возвращает произведение значений X и Y со знаком, вымолненое
в режиме M.
Некоторые машимы поддерживают умножение, при котором произведение имеет
больший размер, чем операнды. Для этого надо писать умножение в следующем
формате:
(mult:M (sign_extend:M X) (sign_extend:M Y))
где M тип, больший, чем типы X и Y, которые не обязаны быть
одинаковыми.
- 46 -
Для расширяющего умножения без знака аналогично, только исспользуя
`zero_extend' вместо 'sign_extend'.
`(div:M X Y)'
Возвращает частное от деления со знаком X на Y, выполненого в режиме
M. Если тип M - тип числа с плавающй точкой то возвращается
точное частное, иначе округленное.
На некоторых машинах имеются инструкции деления в котором тип операндов
и частного не одинаковые. Вы можете употреблять такие инструкции,
исполльзуя `truncate' и `sign_extend' в них:
(truncate:M1 (div:M2 X (sign_extend:M2 Y)))
`(udiv:M X Y)'
Подобно `div' но деление беззнаковое.
`(mod:M X Y)'
`(umod:M X Y)'
Подобно `div' и `udiv' но возвращает остаток, вместо частного.
`(smin:M X Y)'
`(smax:M X Y)'
Возвращает минимум и максимум соответственно. X и Y интрепретируются
как целые числа со знаком типа М.
`(umin:M X Y)'
`(umax:M X Y)'
Подобно `smin' и `smax', но значение интрепретируется как беззнаковое
целое.
`(not:M X)'
Возвращает побитовое дополнние значения X, выполненое в режиме M,
который должен быть режимом с фиксированной точкой.
`(and:M X Y)'
Возвращает побитовое логическое 'и' значений X и Y, выполненое
в режиме M, который должен быть режимом с фиксированной точкой.
- 47 -
`(ior:M X Y)'
Возвращает побитовое исключающее или значений X и Y, выполненое
в режиме M, который должен быть режимом с фиксированной точкой.
`(xor:M X Y)'
Возвращает побитовый XOR значений X и Y, выполненое в режиме M,
который должен быть режимом с фиксированной точкой.
`(ashift:M X C)'
Возвращает смещение знацения X на C бит влево. X имеет тип M - тип
с фиксированной точкой. С должен иметь тип с фисированной точкой или
быть константой типа 'VOIDmode', режим которой определяется из
описания машины как тип для смещения вправо. Например, на Vax,
режим C - 'QImode' независимо от M.
`(lshiftrt:M X C)'
`(ashiftrt:M X C)'
Подобно `ashift', но для сдвига вправо. В отличие от случая сдвига влево,
эти две операции отличны.
`(rotate:M X C)'
`(rotatert:M X C)'
Аналогично, но возвращает левое или правое вращение. Если C -
константа, используйте - `rotate'.
`(abs:M X)'
Возвращает абсолютное значение (модуль) X, вычисленное в режиме M.
`(sqrt:M X)'
Возвращает квадратный корень из X, вычисленный в режиме M.
Обычно M - тип с плавающей точкой.
`(ffs:M X)'
Возвращает один плюс индекс младшего значащего бита в Х, представленное
как целое число типа М. (Если Х-ноль, то возвращается ноль.)
Тип Х не обязательно должен быть М; в зависимости от целевой машины,
могут быть корректными различные комбинации типов.
- 48 -
4.8. Операции сравнения
Операторы сравнения проверяют отношение двух операндов и должны
представлять машинно-независимое ненулевое значение, описываемое с помошью
`STORE_FLAG_VALUE', но не обязательно ему равное (см. Разное) если
отношение выполняется, или ноль, если не выполняется. Тип операции сравнения
не зависит от типа данных, которые она сравнивает. Если результат операции
сравнения используется в проверке (например первый операнд в `if_then_else'),
то тип должен быть `VOIDmode'. Если операция сравнения генерирует данные,
которые должны заноситься в некоторую переменную, то тип должен быть класса
`MODE_INT'. Все операции сравнения, генерирующие данные должны использовать
один тип, которвй зависит от машины.
Существуют два способа, как могут быть использованы операции сравнения.
Операции сравнения могут быть использованы для сравнения кодов условия
`(cc0)' с нулем, как в `(eq (cc0) (const_int 0))'. Такая конструкция
в действительности ссылается на результат предыдущей инструкции, в которой
были установлены коды условий. Инструкция, устанавлвающая код условия
должна быть смежна с инструкцией, использующей код условия; их могут
разделять только 'note' insn.
Напротив, операция сравнения может непосредственно сравнивать два объекта
данных. Тип сравнения определяется операндами; их типы должны восприниматься
как совместимые типы. Сравнение двух константных опрерандов некорректно, т.к.
нельзя понять их тип, но тпкое выражение не должно появляться в RTL, т.к.
оно сразу будет свернуто.
В упомянутом примере, если `(cc0)' был в последний раз установлен
как `(compare X Y)', то операция сравнения идентична '(eq X Y)'. Обычно
на конктретной машине поддерживается только один стиль сравнений, но проход
комбинирования попытается соединить операции для генерации 'eq' в том случае,
если это возможно в контексте данного insn.
Неравенства раздпляются на два типа: знаковые и беззнаковые. Таким
образом, существуют различные коды выражений `gt' и `gtu' для знаковх
и беззнаковых "больше". Они могут выдавать различные результаты для
одинаковых пар целых чисел: например 1 знаково больше чем -1, но не
- 49 -
беззнаково больше. Действительно, -1, когда рассматривается беззнаково
есть `0xffffffff', что больше 1.
Знаковое сравнение используется также для плавающей точки. Вешественные
сравнения различаются по типу операндов.
`(eq:M X Y)'
1 если значения X и Y равны, иначе 0.
`(ne:M X Y)'
1 если значения, представленные X и Y не равны, иначе 0.
`(gt:M X Y)'
1 если X больше Y. Если X и Y - числа с фиксированной точкой то
сравнение выполняется с учетом знака .
`(gtu:M X Y)'
Аналогично `gt' но на числах с фиксированной точкой сравнение
беззнаковое.
`(lt:M X Y)'
`(ltu:M X Y)'
Подобно `gt' и `gtu' но только для 'меньше'.
`(ge:M X Y)'
`(geu:M X Y)'
Подобно `gt' и `gtu' но проверяется 'больше или равен'.
`(le:M X Y)'
`(leu:M X Y)'
Подобно `gt' и `gtu' но проверяется 'меньше или равен'.
`(if_then_else COND THEN ELSE)'
Это - не операция сравнения, но перечислена здесь, потому что она
всегда используется вместе с операциими сравнения. Точнее, COND -
выражение сравнения. Это выражение согласно COND, возвращает значение
THEN или значение ELSE.
- 50 -
На большинстве машин, выражения ` if_then_else ' корректны только,
чтобы выразить условные переходы.
`(cond [TEST1 VALUE1 TEST2 VALUE2 ...] DEFAULT)'
Аналогично 'if_then_else', но более обще. Каждый из TEST1, TEST2,...
выполняется в свою очередь. Результат этого выражения - VALUE,
соответствующее первому тесту отличному от нуля, или DEFAULT, если все
тесты ноль.
Пока для образцов инструкций это еще некорректно и поддерживается
только для insn атрибутов. см. Атрибуты Insn
4.9. Битовые поля.
Существуют специальные выражения для представления конструкций, т.е. они
использующих битовые поля. Эти выражения в RTL являются lvalue - они могут
стоять в левой части присвоения, означая размещения результатов в указанные
битовые поля.
`(sign_extract:M LOC SIZE POS)'
Это выражение представляет ссылку на знако-расширенное битовое поле,
содержащиеся или начинающиеся в LOC (ячейка памяти или регистр).
Битовое поле имеет ширину, равную SIZE, и начинается с бита POS.
Опция трансляции `BITS_BIG_ENDIAN' говорит, с какого конца модуля
памяти отсчитывается POS.
Если LOC находится в памяти, то этот тип должен быть типом однобайтного
целого. Если же LOC - регистр, то используемый тип определяется с
помощью операнда образца 'insv' или `extv' (см. Стандартные имена), и
обычно это целый тип, занимающий слово.
Тип POS является машинно-зависимым и так же определяется в образце
`insv' или `extv'.
Тип M такой же, какой должен был бы использоваться для LOC если бы
это был регистр.
`(zero_extract:M LOC SIZE POS)'
- 51 -
Как `sign_extract', но ссылается на беззнаковое или нуль-расширенное
битовое поле. Извлекается та же самая последовательность битов, но она
заполняет целое слово с нулями вместо знако-расширения.
4.10. Преобразования
Все преобразования типов должны быть представлены явными операциями
преобразования. Например, выражение, которое является суммой байта и слова
не может быть описана следующим образом:
`(plus:SI(reg:QI 34) (reg:SI 80))'
потому что операция 'plus' требует, что бы операнды имели одинаковый тип.
Поэтому байтовый операнд включен в операцию преобразования как:
(plus:SI (sign_extend:SI (reg:QI 34)) (reg:SI 80))
Операция преобразования не просто 'placeholder', т.к. существует
более одного способа преобразования данного типа в конечный. Операция
преобразования указывает на то, как это делать.
Для всех опрераций преобразования X не должен иметь тип `VOIDmode', т.к.
целевой режим в преобразовании должен быть известен. Преобразования должно
быть сделано во время компиляции, или X должен быть помещен в регистр.
`(sign_extend:M X)'
Представляет результат знакового расширения значения X до типа М.
M должен быть типом с фиксированной точкой, а Х невешественным
(с фиксированной точкой) значением с типом, более узким, чем M.
`(zero_extend:M X)'
Представляет результат расширения нулем значения X до типа М.
M должен быть типом с фиксированной точкой, а Х невешественным
(с фиксированной точкой) значением с типом, более узким, чем M.
`(float_extend:M X)'
Представляет результат распространения значения X до типа М.
M должен быть типом с плавающей точкой, а Х вешественным
значением с типом, более узким, чем M.
- 52 -
`(truncate:M X)'
Представляет результат усечения значения X до типа М.
M должен быть типом с фиксированной точкой, а Х невешественным
(с фиксированной точкой) значением с типом, более узким, чем M.
`(float_truncate:M X)'
Представляет результат усечения значения X до типа М.
M должен быть типом с плавающей точкой, а Х вешественным
значением с типом, более узким, чем M.
`(float:M X)'
Представляет результат перевода невешественного значения X, взятого
как знаковое, в вешественный (с плавающей точкой) тип М.
`(unsigned_float:M X)'
Представляет результат перевода невешественного значения X, взятого
как беззнаковое, в вешественный (с плавающей точкой) тип М.
`(fix:M X)'
Если M - невещественный тип, то представляет результат перевода
вешественного значения Х, взятого как знаковое в тип M. Как происходит
округление не определено, поэтому эта операция может быть корректно
использована в коде компиляции С только для заведомо челых аргументов.
`(unsigned_fix:M X)'
Если M - невещественный тип, то представляет результат перевода
вешественного значения Х, взятого как беззнаковое в тип M. Как происходит
округление не определено.
`(fix:M X)'
Если М - вещественный тип, то представляет результат преобазования
вещественного значения Х (корректного в режиме М) к целому, оставаясь
при этом вешественным значением с типом М. Округление происходит в
сторону 0.
4.11. Объявления
Выражения объявлений не представляют собой арифметические операции, но
- 53 -
устанавливают утверждения относительно операндов:
`(strict_low_part (subreg:M (reg:N R) 0))'
Этот код выражения используется только в одном контексте: как
операнд адресата выражения `set'. Кроме того, операнд этого выражения
должен быть не-парадоксальным выражением `subreg '.
Присутствие " strict_low_part " говорит о том, что часть регистра,
значимая в типе N (но не значимая в типе M) не может быть изменена.
Обычно, назначение в такой 'subreg' может иметь неопределенное
действие на остальную часть регистра, если тип М меньше чем слово.
знаковое, в вещественный (с плавающей точкой) тип М.
`(fix:M X)'
Если M - невещественный тип, то представляет результат перевода
вещественного значения Х, взятого как знаковое в тип M. Как происходит
округление не определено, поэтому эта операция может быть корректно
использована в коде компиляции С только для заведомо целых аргументов.
`(unsigned_fix:M X)'
Если M - невещественный тип, то представляет результат перевода
вещественного значения Х, взятого как беззнаковое в тип M. Как происходит
округление не определено.
`(fix:M X)'
Если М - вещественный тип, то представляет результат преобразования
вещественного значения Х (корректного в режиме М) к целому, оставаясь
при этом вещественным значением с типом М. Округление происходит в
сторону 0.
4.12. Выражения с побочным эффектом
Коды выражения, описанные до сих пор, представляют значения, а не действия.
Но машинные команды никогда не производят значения; они значимы только
из-за их побочных эффектов на состоянии машины. Специальные коды выражения
используются, чтобы представить побочные эффекты.
- 54 -
Тело команды - всегда один из этих кодов побочного эффекта;
коды, описанные выше, которые представляют значения, появляются только как
операнды этих.
" (set LVAL X) "
Представляет действие сохранения значения X в место,
представленное LVAL. LVAL должно быть выражением, представляющим
место, в которое можно производить сохранение: " reg " (или " subreg "
или " strict_low_part "), " mem ", " pc " или " cc0 ".
Если LVAL - " reg ", " subreg " или " mem ", оно имеет машинный тип;
тогда X должен подходить для этого типа.
Если LVAL - " reg ", чей машинный тип меньше, чем полная ширина
регистра, то это означает, что части регистра,
определяемой машинным типом, присваивается указанное значение, а
остатку регистра присваивается неопределенное значение. Аналогично, если
LVAL - " subreg ", чей машинный тип меньше, чем тип
регистра, то остальная часть регистра может измениться произвольным
образом.
Если LVAL - " strict_low_part " для " subreg ", то части
регистра, определенной машинным типом " subreg ", присваивается
значение X, а остальная часть регистра не изменяется.
Если LVAL - " (cc0) ", оно не имеет машинного типа, и X может быть
выражением " compare " или значением, которое может иметь любой тип.
Последний случай представляет команду " test ". Выражение " (set
(cc0) (reg:M N)) " эквивалентно выражению " (set (cc0) (compare (reg:M N)
(const_int 0))) ". Используйте вышеупомянутое выражение, чтобы
зарезервировать память во время трансляции.
Если LVAL - " (pc) ", мы имеем команду перехода, и
возможности для X весьма ограничены. Это может быть выражение " label_ref "
(безусловный переход). Это может быть " if_then_else "
(условный переход), в этом случае второй или третий
операнд должен быть " (pc) " (для случая, когда нет перехода), а
- 55 -
другой из них должен быть " label_ref " (для случая, когда есть
переход). X может также быть " mem " или " (plus: SI (pc) Y) ", где Y может
быть " reg " или " mem "; эти необычные варианты используются для
осуществления переходов через таблицы ветвлений.
Если LVAL не является ни " (cc0) ", ни " (pc) ", то тип LVAL должен не
быть " VOIDmode " и тип X должен подходить для типа LVAL.
К LVAL обычно обращаются макрокомандой " SET_DEST ", а к X -
макрокомандой " SET_SRC ".
" (return) "
Единственное выражение в образце, представляющее возврат из
текущей функции, на машинах, где это может быть выполнено одной
командой, типа Vaxes. На машинах, где для возвращения из функции
должен быть выполнен многокомандный "эпилог" ,
возврат выполняется переходом к метке, которая предшествует
эпилогу, и код выражения " return " никогда не используется.
Внутри выражения " if_then_else " представляет значение, которое должно
быть помещено в " pc " для возврата в вызывающую функцию.
Обратите внимание, что образец insn " (return) " логически эквивалентен
" (set (pc) (return)) ", но последняя форма никогда не используется.
" (call FUNCTION NARGS) "
Представляет обращение к функции. FUNCTION - выражение " mem ", чей
адрес - адрес функции, которую нужно вызвать. NARGS -
выражение, которое может использоваться для двух целей: на некоторых машинах
оно представляет число байтов параметров в стеке; на других, оно
представляет число регистров с параметрами.
Каждая машина имеет стандартный машинный тип, который должна иметь FUNCTION.
Машинное описание определяет макрокоманду " FUNCTION_MODE ",
преобразующуюся в необходимое имя типа. Цель этого типа -
указать доступные виды адресации на машинах, где
доступные виды адресации зависят от машинного типа.
- 56 -
" (clobber X) "
Представляет сохранение или возможное сохранение непредсказуемого,
неописанного значения в X, который должен быть выражением " reg ",
" scratch " или " mem ".
Одно из мест, где это используется - в строковых командах, которые
сохраняют стандартные значения в некоторые аппаратные регистры.
Описывать сохраняемые значения не очень-то необходимо, но важно
сообщить транслятору, что регистры будут
изменены, чтобы это он не пытался хранить данные в них во время
исполнения строковой команды.
Если X - " (mem: BLK(const_int 0)) ", это означает что всю память
следует предполагать изменяемой во время вызова.
Обратите внимание, что машинное описание классифицирует определенные
аппаратные регистры как "call-clobbered". Все вызовы функций считаются
по умолчанию использующими эти регистры, так что не нужно
использовать выражения " clobber ", чтобы указать этот факт. Также
предполагается, что каждый вызов функции может изменить любую область
памяти, если функция не объявлена " const ".
Если в последней группе выражений в " parallel " каждое является
выражением " clobber ", с параметрами-выражениями " reg " или " match_scratch "
(* См.: RTL Шаблон::.), при комбинировании могут добавляться
соответствующие выражения " clobber " к создаваемому insn,
если это потребуется для согласования.
Эта особенность может использоваться, например, на машине, которая имеет
команды сложения и умножения, не использующие регистр MQ, но которая
имеет команду добавления, которая задействует MQ регистр. Аналогично,
скомбинированная команда могла бы требовать временный регистр,
а ее составные части - нет.
Когда выражение " clobber " для регистра появляется внутри a
" parallel " с другими побочными эффектами, распределитель регистров
гарантирует, что регистр является незанятым, и до, и после
этого insn. Однако, фаза перезагрузки может распределять регистр,
- 57 -
используемый для одного из вводов, если для выбранного варианта
не указано ограничение " & " (* См.: Модификаторы::.). Вы можете
применять " clobber " для аппаратного регистр, псевдорегистра или
выражения " scratch "; в последних двух случаях, GNU CC распределит доступный
там аппаратный регистр для временного использования.
Для команд, которые требуют временного регистра, Вы должны использовать
" scratch " вместо псевдорегистра, потому что это позволит
фаза комбинирования добавить " clobber " когда требуется. Это делается
кодированием (" clobber " (" match_scratch " ...)). Если Вы используете
" clobber " для псевдорегистра, используйте тот, который больше нигде не
появляется и генерируйте каждый раз новый. Иначе могут смешаться CSE.
Имеется другое известное использование затирания псевдорегистра
в " parallel ": когда один из входных операндов insn также
затирается insn. В этом случае, использование одного и того же
псевдорегистра для затирания и в другом месте в insn приводит к
ожидаемым результатам.
" (use X) "
Представляет использование значения X. Это указывает, что значение
X в этой точке программы необходимо, даже если это может
быть неочевидно. Следовательно, транслятор не будет
пытаться удалять предыдущие команды, которые приводят только к
сохранению значения X. X, должен быть выражением " reg ".
В течение фазы планирования отсроченных переходов X может быть insn.
Это указывает, что X предварительно был размещен в этом месте
кода и зависимость его данных должна быть принята во внимание.
Эти " use " insns будут удалено перед окончанием фазы планирования
отсроченных переходов.
" (parallel [X0 X1 ...]) "
Представляет несколько побочных эффектов, выполняемых параллельно.
Квадратные скобки обозначают вектор; операнд " parallel " является
вектором из выражений. X0, X1 и так далее - индивидуальные выражения с
побочным эффектом - выражения кода " set ", " call ", " return ",
" clobber " или " use ".
- 58 -
" In parallel " означает что сначала все значения, используемые в
Индивидуальные побочные эффекты вычислены, и секунда весь фактический
Побочные эффекты выполняются. Например,
(parallel [(set (reg: SI 1) (mem: SI (reg: SI 1)))
(set (mem: SI (reg: SI 1)) (reg: SI 1))])
недвусмысленно говорит, что значения аппаратного регистра 1 и
области памяти, адресованной им, обмениваются местами. В обоих местах,
где " (reg: SI 1) " появляется как адрес памяти, это относится к
значению в регистре 1 *перед* выполнением insn.
Из этого следует, что *неправильно* использовать " parallel " и ожидать,
что результат одного " set " будет доступен для следующего. Например,
команду "перейти, если ноль" иногда пытаются представлять так:
(parallel [(set (cc0) (reg: SI 34))
(set (PC) (if_then_else
(eq (cc0) (const_int 0))
(label_ref ...)
(pc)))])
Но это неправильно, потому что такой код говорит, что условие перехода
зависит от значения кода условия *перед* этой командой, а не от
нового значения, которое устанавливается этой командой.
Решетчатая оптимизация, которая происходит вместе с заключительным
выводом ассемблерного кода, может производить insns, чьи образцы состоят
из " parallel ", элементы которых - операнды, необходимые для вывода
возникающего в результате ассемблерного кода - часто " reg ", " mem "
или константные выражения. Это не был бы правильно построенный RTL в любой
другой стадии трансляции, но в этом случае все нормально, потому что
в дальнейшем никакой оптимизации не производится. Однако, определение
макрокоманды " NOTICE_UPDATE_CC ", если оно имеется, должно иметь дело
с такими insns если Вы определяете какую-либо решетчатую оптимизацию.
" (sequence [INSNS ...]) "
- 59 -
Представляет последовательность insns. Каждое из INSNS, которое
появляется в векторе, подходит для появления в цепочке insns, так что оно
должно быть " insn ", " jump_insn ", " call_insn ", " code_label ",
" barrier " или " note ".
" sequence " RTX никогда не помещается в фактический insn во время
генерации RTL. Оно представляет последовательность insns, которые
получаются " define_expand " *прежде*, чем те insns передаются
" emit_insn " для вставки их в цепочку insns. При фактической вставке
отдельные sub-insns выделяются и " sequence " исчезает.
После того, как планирование слотов задержки завершено, insn и весь
insns, которые находятся в слотах задержки группируются вместе в
" sequence ". Insn требует слот задержки первым insn в векторе;
последующий insns должны быть помещены в слот задержки.
" INSN_ANNULLED_BRANCH_P " устанавливается на insn в слоте задержки,
чтобы указать, что insn перехода должен использоваться, что будет
условно аннулировать эффект insns в слотах задержки. В таком случае,
" INSN_FROM_TARGET_P " указывает, что insn - из адреса
перехода и должен выполняться только, если переход происходит;
иначе insn должен выполняться только, если переход не происходит.
* См.: Слоты Задержки::.
Эти коды выражения появляются вместо побочного эффекта, как тело
insn, хотя, строго говоря, они не всегда описывают побочные
эффекты как таковые:
" (asm_input S) "
Представляет литеральный код ассемблера с помощью строки S.
" (unspec [OPERANDS ...] INDEX) "
" (unspec_volatile [OPERANDS ...] INDEX) "
Представляет машинно-специфическую операцию над OPERANDS. INDEX
задает машинно-специфическую операцию.
" unspec_volatile " используется для volatile операций и операций,
которые могут захватывать; " unspec " используется для остальных операций.
- 60 -
Эти коды могут появляться внутри " pattern " insn, внутри
" parallel " или внутри выражения.
" (addr_vec: M [LR0 LR1 ...]) "
Представляет таблицу адресов перехода. Векторные элементы LR0,LR1
и т.д., являются выражениями " label_ref ". Тип M определяет, сколько
места выделяется каждому адресу; обычно M должен быть " Pmode ".
" (addr_diff_vec: M BASE [LR0 LR1 ...]) "
Представляет таблицу адресов перехода, выраженных как смещения от
BASE. Векторные элементы LR0,LR1 и т.д. являются выражениями " label_ref ",
и BASE тоже.Режим M определяет, сколько места выделяется каждому адресу.
4.13. Вложенные побочные эффекты на адресах
Четыре специальных кода выражения побочного эффекта появляются как адреса памяти.
" (pre_dec: MX) "
Представляет побочный эффект уменьшения X на стандартное число,
а также значение, которое X имеет после уменьшения.
X должен быть " reg " или " mem ", но большинство машин позволяет только " reg ".
М должен быть машинным типом для указателей,используемым на машине.
Число, на которое уменьшается X - длина в байтах
машинного типа ссылки памяти, для которой это
выражение служит адресом. Вот пример использования:
(mem: DF (pre_dec: SI (reg: SI 39)))
Это означает уменьшение псевдорегистра 39 на длину значения
" DFmode " и использование результата для адресации значения " DFmode ".
" (pre_inc: M X) "
Аналогично, происходит не уменьшение, а увеличение X.
" (post_dec: M X) "
Представляет тот же самый побочный эффект, что и " pre_dec ", но другое
значение. Значение, представляемое здесь - значение X перед
- 61 -
уменршением.
" (post_inc: M X) "
Аналогично, происходит не уменьшение, а увеличение X.
Эти вложенные выражения побочного эффекта должны использоваться осторожно.
Образцы команд не могут использовать их. До прохода " flow "
транслятора, они могут иметь место только для помещения значений на стек.
Проход " flow " находит случаи, когда регистры увеличиваются или уменьшаются
в одной команде и используются как адрес непосредственно перед этим или после
этого; эти места преобразуются для использования pre- или post- увеличения или
уменьшения.
Если регистр, используемый как операнд этих выражений, используется для
другого адреса в insn, используется первоначальное значение регистра.
Использование регистра вне адреса внутри того же самого insn не разрешается,
так же, как и использование в выражении с вложенным побочным эффектом, потому что
такие insns ведут себя по-разному на разных машинах и, следовательно, должны
обрабатываться неоднозначно и запрещаются.
Команду, которая может быть представлена при помощи вложенного побочного эффекта,
можно также представить, используя " parallel ", содержащий дополнительный
" set ", чтобы описать, как изменяется регистр адреса. Это не
сделано, потому что машины, которые вообще позволяют эти операции, обычно
позволяют их всюду, где запрашивается адрес памяти. Описание их как
дополнительных сохранений " parallel " потребовало бы удвоения числа входов
в машинном описании.
4.14. Команды Ассемблера как выражения
RTX код " asm_operands " представляет значение, получившееся из
определенной пользователем команды ассемблера. Это используется для представления
операторов " asm " с параметрами. Операторы " asm " с одиночным операндом
вывода, типа этого:
asm ("foo %1, %2, %0" : "=a" (outputvar): "g" (x + y), "di" (*z));
- 62 -
представляются с использованием одиночного " asm_operands " RTX, который
представляет значение, которое сохранено в " outputvar ":
(set RTX-FOR-OUTPUTVAR
(asm_operands "foo %1, %2, %0" "a" 0
[RTX-FOR-ADDITION-RESULT RTX-FOR-*Z]
[(asm_input: M1 "g")
(asm_input: M2 "di")]))
Здесь операнды " asm_operands " RTX - шаблон ассемблерной
строки, ограничение выходного операнда, индексный номер выходного операнда
среди указанных выходных операндов, вектор входного операнда
RTX и вектор типов и ограничений входного операнда. Режим
M1 - тип суммы " x+y "; M2 - тип для "*z".
Когда оператор " asm " имеет несколько значений для вывода, его insn имеет
несколько таких RTX " set " внутри " parallel ". Каждый " set " содержит
" asm_operands "; все они совместно используют один и тот же ассемблерный шаблон и
векторы, но каждый содержит ограничение для соответствующего операнда вывода.
Они также различаются по индексу операнда вывода,
который равен 0, 1, ... для последовательных операндов вывода.
4.15. Insns
RTL представление кода функции - связанная в две стороны
цепочка объектов, называемых "insns". Insns - выражения со специальными
кодами, которые не используются ни для какой другой цели. Некоторые insns
фактически являются командами; другие представляют таблицы управления для
операторов " switch "; другие представляют метки перехода или различных
видов описаний.
В дополнение к собственным специфическим данным, каждый insn должен иметь уникальный
идентифицирующий номер, который отличает его от всех других insns в текущей
функции (после отсроченного планирования перехода копии insn с
одинаковыми идентифицирующими номерами могут присутствовать в нескольких
местах в функции, но эти копии всегда будут идентичны и будут появляться только
внутри " sequence "), и указатели на предыдущий и следующий insn для получения
- 63 -
цепочки. Эти три поля занимают одну и ту же позицию в каждом insn, независимо
от кода выражения insn. Вообще говоря, к ним можно обращаться при помощи " XEXP "
и " XINT ", но вместо них всегда используется три специальных макрокоманды:
" INSN_UID (I) "
Обращается к уникальному идентификатору insn I.
" PREV_INSN (I) "
Обращается к указателю цепочки на insn, предшествующий I. Если I -
первый insn, это - нулевой указатель.
" NEXT_INSN (I) "
Обращается к указателю цепочки на insn после I. Если I -
последний insn, это - нулевой указатель.
Первый insn в цепочке получается вызовом " get_insns ";
последний insn - вызовом " get_last_insn ". Внутри цепочки,
ограниченной этими insns, " NEXT_INSN " и " PREV_INSN " указатели всегда
должны соответствовать друг другу: если INSN - не первый insn,
NEXT_INSN (PREV_INSN (INSN)) == INSN
всегда справедливо, и если INSN - не последний insn,
PREV_INSN (NEXT_INSN (INSN)) == INSN
всегда справедливо.
После планирования слотов задержки, некоторые из insns в цепочке могут быть
выражениями " sequence ", которые содержат вектор insns. Значение
" NEXT_INSN " для всех, кроме последнего, из этих insns - следующий insn в
вектор; значение " NEXT_INSN " для последнего insn в векторе -
такое же, как и значение " NEXT_INSN " для " sequence ", в котором он
содержится. Аналогичные правила соблюдаются для " PREV_INSN ".
Это означает, что вышеупомянутые инварианты - не обязательно истина для
insns внутри выражений " sequence ". А именно, если INSN -
первый insn в " sequence ", " NEXT_INSN (PREV_INSN (INSN)) " - это insn,
- 64 -
содержащий выражение " sequence ", а значение " PREV_INSN( NEXT_INSN (INSN)) "
- это insn, являющийся последним в выражении " sequence ". Вы можете
использовать эти выражения, чтобы найти выражение " sequence ", содержащее
данный insn.
Каждый insn имеет один из следующих шести кодов выражения:
" insn "
Код выражения " insn " используется для команд, которые не делают
переходов и обращений к функциям. Выражения " sequence "
всегда содержатся в insns с кодом " insn ", даже если один из их
insns делает переход или обращение к функции.
Insns с кодом " insn " имеют четыре дополнительных поля после трех
обязательных, перечисленных выше. Эти четыре поля описаны ниже в таблице.
" jump_insn "
Код выражения " jump_insn " используется для команд, которые могут
делать переход (или, более общо, могут содержать выражения " label_ref ").
Команда возврата из текущей функции регистрируется как " jump_insn ".
" jump_insn " insns имеют те же самые дополнительные поля, что и " insn " insns,
обращение к которым производится таким же образом, и, кроме того, содержат поле
" JUMP_LABEL ", которое определяется после окончания оптимизации перехода.
Для простого условного и безусловного перехода это поле
содержит " code_label ", к которому этот insn будет производить (возможно,
условный) переход. В более сложном переходе, в " JUMP_LABEL "
записывается одна из меток, к которым insn обращается; единственый путь
для того, чтобы найти остальные - просмотреть все тело insn.
insns возврата рассматриваются как переходы, но так как они не
обращаются ни к каким меткам, они имеют ноль в поле " JUMP_LABEL ".
" call_insn "
Код выражения " call_insn " используется для команд, которые могут делать
обращения к функции. Важно отличать эти команды, потому что они подразумевают,
что некоторые регистры и области памяти могут непредсказуемо измениться.
- 65 -
" call_insn " insns имеют те же самые дополнительные поля, что и " insn "
insns, обращение к которым производится таким же образом, и, кроме того,
содержат поле " CALL_INSN_FUNCTION_USAGE ", который содержит список
(цепочка выражений " expr_list "), содержащий " use " и " clobber "
выражения, которые обозначают аппаратные регистры, используемые или
затираемые вызываемой функцией. Регистр, указанный в " clobber " в этом списке,
изменяется *после* выполнения " call_insn ", в то время как
регистр в " clobber " в теле " call_insn " затирается прежде, чем insn
завершает выполнение. " clobber "
выражения в этом списке увеличивают регистры, указанные в
" CALL_USED_REGISTERS " (* См.: Основные сведения о регистрах::.).
" code_label "
" code_label " insn представляет метку, на которую возможен переход
при помощи insn перехода. Он содержит два специальных поля данных в дополнение к
трем стандартным. " CODE_LABEL_NUMBER " используется для содержания
" label number ", номер, который однозначно идентифицирует эту метку среди
всех меток в трансляции (не только в текущей функции). В конечном счете,
метка представляется на ассемблерном выводе как метка ассемблера, обычно
в форме " LN ", где N - номер метки.
Когда " code_label " появляется в выражении RTL, оно обычно
появляется внутри " label_ref ", которое представляет адрес
метки как номер.
Поле " LABEL_NUSES " определяется только после завершения фазы
оптимизации перехода и содержит количество ссылок на эту метку
в текущей функции.
" barrier "
Барьеры помещаются в поток команд, если управление не может
передаваться коду после них. Они помещаются после команды безусловного
перехода, чтобы указать, что переходы являются безусловными, и
после обращений к функциям " volatile ", которые не возвращаются (например,
" exit "). Они не содержат никакую информацию, кроме трех стандартных
полей.
- 66 -
" note "
" note " insns используются для представления дополнительной отладочной и
декларативной информации. Они содержат два нестандартных поля,
целое число, к которому обращаются макрокомандой " NOTE_LINE_NUMBER " и
строку, к которой обращаются " NOTE_SOURCE_FILE ".
Если " NOTE_LINE_NUMBER " положительно, примечание представляет
номер исходной строки, а " NOTE_SOURCE_FILE " - имя исходного файла,
содержащего эту строку. Эти примечания управляют генерацией
номеров строк в ассемблерном выводе.
В противном случае, " NOTE_LINE_NUMBER " не реальный номер строки, а
код с одним из следующих значений (а " NOTE_SOURCE_FILE " должен
содержать нулевой указатель):
" NOTE_INSN_DELETED "
Такие примечания полностью игнорируются. Некоторые проходы
транслятора удаляют insns, преобразуя их в примечания этого вида.
" NOTE_INSN_BLOCK_BEG "
" NOTE_INSN_BLOCK_END "
Эти типы примечаний указывают позицию начала
и конца уровня обзора имен переменных. Они
управляют выводом информации об отладке.
" NOTE_INSN_LOOP_BEG "
" NOTE_INSN_LOOP_END "
Эти типы примечаний указывают позицию начала и конца циклов
"while " и " for ". Они позволяют оптимизатору циклов быстро
находить циклы.
" NOTE_INSN_LOOP_CONT "
Появляется в месте цикла, на которое делает переход оператор
" continue ".
" NOTE_INSN_LOOP_VTOP "
Это примечание указывает место в цикле, где начинается проверка на
выход для тех циклов, в которых проверка на выход продублирована.
- 67 -
Эта позиция становится другим виртуальным началом
цикла при рассмотрении инвариантов цикла.
" NOTE_INSN_FUNCTION_END "
Появляется около конца тела функции, непосредственно перед
меткой, на которую переходит оператор " return " (на машинах, на
которых одиночной команды не достаточно для возврата). Это
примечание может быть удалено оптимизацией перехода.
" NOTE_INSN_SETJMP "
Появляется после каждого обращения к " setjmp " или
соответствующей функции.
Эти коды печатаются символически, когда они появляются в отладочных
дампах.
Машинный тип insn - обычно " VOIDmode ", но некоторые фазы
используют тип для различных целей; например, проход перезагрузки устанавливает
его в " HImode ", если insn требуется перезагрузка, но не удаление регистров,
и " QImode ", если требуется и то, и другое. Проход общего удаления подвыражений
устанавливает тип insn в " QImode ", если это первый insn в блоке, который
уже был обработан.
Вот таблица дополнительных полей " insn ", " jump_insn " и
" call_insn " insns:
" PATTERN (I) "
Этим insn выполняется выражение c побочным эффектом. Это
должен быть один из следующих кодов: " set ", " call ", " use ",
" clobber ", " return ", " asm_input ", " asm_output ", " addr_vec ",
" addr_diff_vec ", " trap_if ", " unspec ", " unspec_volatile ",
" parallel " или " sequence ". Если это " parallel ", каждый элемент
" parallel " должен быть одним из этих кодов, за исключением того, что
" parallel " выражения не могут быть вложены и " addr_vec " и " addr_diff_vec "
не разрешаются внутри выражения " parallel ".
" INSN_CODE (I) "
Целое число, которое сообщает, какие образцы в машинном описании
- 68 -
соответствуют этому insn, или -1, если соответствие еще не
устанавливалось.
Такое соответствие никогда не устанавливается и это поле остается -1 для
insn, чей образец состоит из одиночного выражения " use ", " clobber ",
" asm_input ", " addr_vec " или " addr_diff_vec ".
Соответствие также никогда не устанавливается для insns, которые
получаются из операторов " asm ". Они содержат по крайней мере одно выражение
" asm_operands ". Функция " asm_noperands " возвращает неотрицательное значение
для таких insns.
При отладочном выводе это поле печатается как число с
последующим символическим представлением, которое определяет
месторасположение образца в " md " файле так некоторое маленькое
положительное или отрицательное смещение от указанного образца.
" LOG_LINKS (I) "
Список (цепочка выражений " insn_list ") предоставляет информацию
о зависимостях между командами внутри базисного блока. Между связанным
insns не может быть ни перехода, ни метки.
" REG_NOTES (I) "
Список (цепочка выражений " expr_list " и " insn_list ") предоставляет
различную информацию относительно insn. Часто это информация
относительно регистров, используемых в этом insn.
Поле " LOG_LINKS " insn - это цепочка " insn_list " выражений. Каждое из
них имеет два операнда: первое - insn, а второе - другое выражение
" insn_list " (следующее в цепочке). Последний " insn_list " в цепочке имеет
нулевой указатель в качестве второго операнда. Существенно, какие insns
появляются в цепочке как первые операнды выражений " insn_list ". Их порядок
не имеет значения.
Этот список первоначально устанавливается проходом потокового анализа;
до него это нулевой указатель. Потокоый анализ только добавляет связи для тех
зависимостей данных, которые могут использоваться для комбинации команд. Для
каждого insn проход потокового анализа добавляет связь с insns, которые сохраняют в
- 69 -
регистры значения, которые используются впервые в этом insn.
проход планирования команд добавляет дополнительные связи так, чтобы каждая
зависимость была представлена. Связи представляют зависимости данных,
антизависимости и зависимости вывода; машинный тип связи различает эти три
типа: антизависимости имеют тип " REG_DEP_ANTI ", зависимости вывода имеют тип
" REG_DEP_OUTPUT ", и зависимости данных имеют тип " VOIDmode ".
Поле " REG_NOTES " insn - цепочка, подобная полю
" LOG_LINKS ", но оно включает, помимо выражений " insn_list ", выражения
" expr_list ". Имеются несколько видов регистровых примечаний,
которые различаются машинным типом, который в регистровом примечании
действительно понимается как являющийся " enum reg_note ". Первый операнд OP
примечания - данные, чья интерпретация зависит от вида примечания.
Макрокоманда " REG_NOTE_KIND (X) " возвращает вид регистрового примечания.
Парная к ней макрокоманда " PUT_REG_NOTE_KIND (X, NEWKIND) " устанавливает
тип регистрового примечания X в NEWKIND.
Регистровые примечания бывают трех классов: они могут говорить что-либо о
вводе в insn, говорить что-либо о выводе insn или создавать связи между
двумя insns. Имеется также набор значений, которые используются только в
" LOG_LINKS ".
Эти регистровые примечания аннотируют ввод в insn:
" REG_DEAD "
Значение в OP умирает в этом insn; то есть изменение
значения немедленно после этого insn не воздействовало бы на дальнейшее
поведение программы.
Это не обязательно означает, что регистр OP не содержит никакого полезного
значения после этого insn, так как это может также быть вывод insn.
В таком случае, однако, " REG_DEAD " примечание было бы избыточно и
обычно не представляется до окончания прохода перезагрузки, но никакой
код не использует этот факт.
" REG_INC "
Регистр OP увеличивается (или уменьшается; на этом уровне не имеется
- 70 -
никакого различия) вложенным побочным эффектом внутри этого insn. Это
означает, что он появляется в выражении " post_inc ", " pre_inc ",
" post_dec " или " pre_dec ".
" REG_NONNEG "
Регистр OP имеет неотрицательное значение, когда этот insn достигается.
Это используется так, что команды декремента и перехода до нуля, типа
m68k dbra, могут быть согласованы.
" REG_NONNEG " примечание добавляется к insns, только если машинное
описание имеет образец " decrement_and_branch_until_zero ".
" REG_NO_CONFLICT "
Этот insn не вызывает конфликта между OP и элементом, устанавливаемым
этим insn, даже если имеет такую возможность.
Другими словами, если регистру-адресату и OP мог бы быть иначе
присвоен один и тот же регистр, этот insn не предотвращает этого
присваивания.
Insns с этим примечанием - обычно часть блока, который начинается с a
" clobber " insn, указывающего псевдорегистр из нескольких слов (который
будет выводом блока), группа insns, каждое из которых устанавливает одно
слово значения и имеет примечание " REG_NO_CONFLICT ",
и заключительное insn, которое копирует вывод к себе с примечанием
" REG_EQUAL ", вычисляя выражение. Этот блок
инкапсулируется примечаниями " REG_LIBCALL " и " REG_RETVAL " для
первого и последнего insns, соответственно.
" REG_LABEL "
Этот insn использует OP, " code_label ", но - не " jump_insn ".
присутствие этого примечания позволяет оптимизации перехода знать что OP,
на самом деле, используется.
Следующие примечания описывают атрибуты выводов insn:
" REG_EQUIV "
" REG_EQUAL "
Это примечание имеет силу только для insn, которое устанавливает только
- 71 -
один регистр и указывает, что этот регистр будет равен OP во время выполнения;
контекст этой эквивалентности различается в зависимости от типа примечаний.
Значение, которое insn явно копирует в регистр, может казаться
отличным от OP, но они будут равны во время выполнения. Если
вывод одиночных " set " является выражением " strict_low_part ",
примечание относится к регистру, который содержится в " SUBREG_REG "
выражения " subreg ".
Для " REG_EQUIV " регистр эквивалентен OP полностью во всей функции, и
все его вхождения можно законно заменить на OP. ("Законно" здесь
относится к потоку данных программы; простая замена может делать некоторые
insns недопустимыми.) Например, когда константа загружается в регистр,
которому больше никогда не присваивается никакое другое значение,
используется этот вид примечания.
Когда параметр копируется в псевдорегистр при входе в
функцию, примечание этого вида означает, что регистр
равен слоту стека, в котором передается параметр.
Хотя в этом случае регистр может быть установлен другими insns,
по-прежнему возможно заменить регистр слотом стека
во время выполнения функции.
В случае " REG_EQUAL ", регистр, который устанавливается этим insn,
будет равен OP во время выполнения в конце этого insn, но не
обязательно в другом месте в функции. В этом случае OP
обычно арифметическое выражение. Например, когда используется
последовательность insns типа вызова библиотеки для выполнения
арифметической операции, этот вид примечания присоединена к insn, которое
производит или копирует значение-результат.
Эти два примечания используются различными способами проходами транслятора.
" REG_EQUAL " используется проходами до распределения регистров (такими,
как общее удаление подвыражений и оптимизация цикла), чтобы сообщить
им, как думать об этом значении. " REG_EQUIV " примечания используются
во время распределения регистров, чтобы указать, что имеется доступное
выражение замены (или константное или " mem " выражение для
расположения параметра на стеке), которое может использоваться вместо
регистра, если доступно недостаточное количество регистров.
- 72 -
Кроме расположения параметров в стеке, которое указывается примечанием
" REG_EQUIV " и не используется при проходах ранней оптимизации
и псевдорегистров, которые являются эквивалентными ячейке памяти
в течение всего времени своего существования, которые пока не обнаружены
при трансляции, все эквивалентности первоначально обозначаются
примечанием " REG_EQUAL ". На ранних стадиях распределения регистров
примечание " REG_EQUAL " изменяется на примечание " REG_EQUIV ",
если OP - константа, и insn представляет единственый набор
его регистров назначения.
Таким образом, проходы транслятора до распределения регистров должны
проверять только примечания " REG_EQUAL ", а проходы, следующие после
распределения регистров, должны проверять только примечания
" REG_EQUIV ".
" REG_UNUSED "
Регистр OP, устанавливаемый этим insn, не будет использоваться в
последующих insn. Это отличается от примечания " REG_DEAD ", которое
указывает, что значение во вводе не будет использоваться впоследствии.
Эти два примечания независимы и могут оба присутствовать для одного и
того же регистра.
" REG_WAS_0 "
Одиночный вывод этого insn, содержащий ноль перед этим insn.
OP - insn, устанавливающий его в ноль. Вы можете полагаться на это примечание,
если оно присутствует и OP не был удален или превращен в примечание ;
его отсутствие не подразумевает ничего.
Эти примечания описывают связи между insns. Они встречаются парами:
одно insn имеет одно примечание, которое указывает на другое insn, которое
имеет обратное примечание, указывающее обратно на первое insn.
" REG_RETVAL "
Этот insn копирует значение последовательности из многих insn (например,
вызова библиотеки), и OP - первый insn последовательности (для вызова
библиотеки - первый insn, который был сгенерирован для установки
параметров вызова библиотеки).
- 73 -
Оптимизация циклов использует это примечание для обработки такой
последовательности, как одиночная операция
для перемещения кода, а потоковый анализ использует
это примечание, чтобы удалить последовательности, результаты которых
"мертвы".
Примечание" REG_EQUAL " будет также обычно присоединяться к этому insn
для обеспечения выражения, вычисляемого последовательностью.
" REG_LIBCALL "
Это - инверсия " REG_RETVAL ": оно помещается в первый
insn последовательности из многих insn и указывает на последний.
" REG_CC_SETTER "
" REG_CC_USER "
На машинах, которые используют " cc0 ", insns, которые устанавливают и используют
" cc0 ", установка и использование " cc0 " являются смежными.
Однако по окончании заполнения слотов задержки перехода
это уже может не быть так. В этом случае примечание
" REG_CC_USER " будет помещено в insn, устанавливающий " cc0 ", для
указания insn, использующего " cc0 ", а примечание " REG_CC_SETTER "
будет помещено в insn, использующий " cc0 ", для указания insn,
устанавливающего " cc0 ".
Эти значения используются только в поле " LOG_LINKS ", и указывают
тип зависимости, которую представляет каждая связь. Связи, которые указывают
зависимость данных (типа зависимости чтение после записи) не используют
никакого кода, они просто имеют тип " VOIDmode " и печатаются без всякого
описательного текста.
" REG_DEP_ANTI "
Указывает антизависимость (типа зависимости запись после чтения).
" REG_DEP_OUTPUT "
Указывает зависимость вывода (типа зависимости запись после записи).
Для удобства, машинный тип в " insn_list " или " expr_list "
- 74 -
печатается в отладочных дампах с использованием этих символических кодов.
Единственое различие между кодами выражения " insn_list " и
" expr_list " - то, что первый операнд " insn_list " считается
insn и печатается в отладочных дампах как уникальный идентификатор insn;
первый операнд " expr_list " печатается обычным способом как выражение.
4.16. RTL Представление Insns - Вызовов Функций
Insns, которые вызывают подпрограммы, имеют код выражения RTL " call_insn ".
Эти insns должен удовлетворять специальным правилам, и их тела должны
использовать специальный код выражения RTL, " call ".
Выражение " call " имеет два операнда, а именно:
(call (mem: FM ADDR) NBYTES)
NBYTES - операнд, который представляет число байтов
данных параметров, передаваемых подпрограмме, FM - машинный тип
(который должен совпадать с определением макрокоманды " FUNCTION_MODE " в
машинном описании), а ADDR представляет адрес подпрограммы.
Для подпрограммы, которая не возвращает значения, выражение " call ", как
показано выше, есть все тело insn, за исключением того, что insn может
также содержать выражения " use " или " clobber ".
Для подпрограммы, которая возвращает значение, чей тип - не " BLKmode ",
значение возвращается в аппаратном регистре. Если номер этого регистра
R, то тело insn вызова выглядит примерно так:
(set (reg: M R)
(call (mem: FM ADDR) NBYTES))
Это выражение RTL поясняет (проходам оптимизатора), что
соответствуюему регистру присваивается полезное значение в этом insn.
Когда подпрограмма возвращает значение " BLKmode ", это обрабатывается
- 75 -
путем передачи подпрограмме адреса места, в которое следует записать значение.
Так что само insn вызова не " возвращает " никакого значения и имеет
тот же самый вид RTL, как и вызов, который не возвращает ничего.
На некоторых машинах команда вызова непосредственно задействует некоторые
регистры, например для хранения адреса возврата. " call_insn " insns на этих
машинах должны иметь тело, которое является " parallel ", содержащим как
выражение " call ", так и выражения " clobber ", которые указывают, значения
каких регистров теряются. Аналогично, если команде вызова необходим
некоторый регистр, отличный от указателя стека, который не упомянут явно
в его RTL, он должен быть упомянут в подвыражении " use ".
Предполагается, что вызываемые функции изменяют все регистры, перечисленные
в макрокоманде конфигурации " CALL_USED_REGISTERS " (* См.: Основные сведения
о регистрах::.) и, за исключением функций " const " и библиотеки
вызовов, изменяют всю память.
Insns, содержащие только выражения " use ", непосредственно предшествуют
" call_insn " insn, чтобы указать, какие регистры содержат входные данные
для функции. Аналогично, если регистры, отличные от указанных в
" CALL_USED_REGISTERS ", затираются вызываемой функцией, insns
содержащие одиночный " clobber ", следуют немедленно после вызова, чтобы
указать такие регистры.
4.17. Предположения о Совместном Использовании Структур
Транслятор считает, что некоторые виды выражений RTL
уникальны; не существует двух различных объектов, представляющих одно и то же
значение. В других случаях он делает противоположное предположение: никакой
объект RTL выражения определенного вида не появляется более чем в одном месте
в объемлющей структуре.
Эти предположения относятся к одиночной функции; кроме RTL
объектов, которые описывают глобальные переменные и внешние функции, и
нескольких стандартных объектов типа маленьких целочисленных констант, никакие объекты RTL
не являются общими для двух функций.
- 76 -
* Каждый псевдорегистр имеет только один объект " reg " для его
представления и, следовательно, только один машинный тип.
* Для любой символической метки, имеется только один объект " symbol_ref ",
ссылающийся на нее.
* Имеется только одно выражение " const_int " со значением 0, только одно
со значением 1 и только одним со значением -1. Некоторое другое целое число
Значения также сделаны уникальными.
* Имеется только одно выражение " pc ".
* Имеется только одно выражение " cc0 ".
* Имеется только одно выражение " const_double " со значением 0 для каждого
типа с плавающей точкой. То же справедливо для значений 1 и 2.
* Никакой " label_ref " или " scratch " не появляется более чем в одном
месте в структуре RTL; другими словами, безопасно делать обход дерева всех
insns в функции и считать, что каждый обнаруженный " label_ref "
или " scratch " отличается от других.
* Только один объект " mem " обычно создается для каждой статической переменной
или слота стека, так что эти объекты часто совместно используются во всех
местах, где они появляются. Однако иногда для таких переменных делаются
отдельные, хотя и равные объекты.
* Когда один оператор " asm " имеет несколько операндов вывода, для всех
операндов вывода делаются различные выражения " asm_operands ".
Однако, они все совместно используют вектор, который содержит последовательность
входных операндов. Это используется позже, чтобы проверить, верно ли, что
два выражения " asm_operands " - из одного и того же оператора, так что
все оптимизации должны тщательно сохранять совместное использование,
если они копируют вектор вообще.
* Никакой объект RTL не появляется в больше чем одном месте в структуре RTL
за исключением случаев, описанных выше. Много проходов транслятора полагаются
на это, принимая, что они могут изменять объекты RTL без
- 77 -
нежелательных побочных эффектов для других insns.
* В течение начальной генерации RTL общедоступнная структура
представлена свободно. После того, как весь RTL для функции будет
сгенерирован, вся общедоступнная структура копируется " unshare_all_rtl "
в " emit-rtl.c ", после чего гарантируется следование вышеупомянутых
правил.
* В течение прохода комбинирования общедоступнная структура внутри insn
может существовать временно. Однако, общедоступнная структура
копируется перед окончанием комбинирования для insn. Это делается вызовом
" copy_rtx_if_shared ", который является подпрограммой " unshare_all_rtl ".
4.18. Чтение RTL
Чтобы читать объект RTL из файла, вызовите " read_rtx ". Ему требуется один
параметр, поток стандартного ввода, а возвращает он один объект RTL.
Чтение RTL из файла происходит очень медленно. Это не проблема в настоящее
время, поскольку чтение RTL происходит только как часть формирования
транслятора.
Люди часто представляют использование RTL, сохраненного как текст в файле,
как интерфейс между внешним интерфейсом языка и большей частью GNU
CC. Эта идея не выполнима.
GNU CC был разработан, чтобы использовать RTL только внутренне. Корректный
RTL для данной программы очень сильно зависит от конкретной целевой машины. И
RTL не содержит всей информации о программе.
Соответствующий способ связывать с помощью интерфейса GNU CC на новый внешний интерфейс языка
Со структурой данных "дерева". Не имеется никакого руководства для этой
структуры данных, но она описана в файлах " tree.h " и " tree.def ".
.- 78 -
5. Машинные Описания
Машинное описание имеет две части: файл образцов команд
( ".md" файл) и файл заголовка C макроопределений.
".md" файл для целевой машины содержит образец для каждой
команды, поддерживаемой целевой машиной (или, по крайней мере, для каждой
команда, о которой стоит сообщать транслятору). Он может также
содержать комментарии. Часть строки после точки с запятой является
комментарием, если точка с запятой не находится внутри строки, ограниченной
кавычками.
См. следующую главу для информации о файле заголовка C.
5.1. Все про Образцы Команд
Каждый образец команды содержит незавершенное выражение RTL, с
частями, которые будут заполнены позже, ограничениями на операнды, которые
говорят,как могут быть заполнены эти части, и образец вывода или C код,
чтобы сгенерировать вывод ассемблера, который полностью выполняется в
выражении " define_insn ".
" define_insn " является выражением RTL, содержащим четыре или пять
операндов:
1. Опциональное имя. Присутствие имени указывает, что этот
образец команды может выполнять некоторую стандартную работу для
проход генерации RTL транслятора. Этот проход знает определенные
имена и использует образцы команд с этими именами, если имена
определены в машинном описании.
Отсутствие имени обозначется пустой строкой на том месте, где
должно быть имя. Безымянные образцы команд никогда не
используются для генерации кода RTL, но они могут позволять
нескольким более простым insns объединяться впоследствии.
- 79 -
Имена, которые не определены таким образом и используются в генерации RTL,
не имеют эффекта; они эквивалентны отсутствию имени вообще.
2. " RTL шаблон " (* См.: RTL Шаблон::.) - вектор из
незавершенных выражений RTL, которые показывают, как команда должна
выглядеть. Они незавершенные, потому что могут содержать
" match_operand ", " match_operator " и " match_dup " выражения
в позициях для операндов команды.
Если вектор имеет только один элемент, этот элемент - шаблон
для образца команды. Если вектор имеет несколько элементов,
то образец команды - выражение " parallel ", содержащее
описанные элементы.
3. Условие. Это - строка, которая содержит выражение C, которое
является тестом для окончательного решения, соответствует ли тело insn
этому образцу.
Для именованного образца, условие (если оно есть) не может зависеть на
данных в согласовываемом insn, а может зависеть только от флагов
целевой машины. Транслятор должен проверять эти
условия во время инициализации, чтобы точно знать, какие
поименованные команды доступны для частичного запуска.
Для безымянных образцов условие применяется только тогда, когда
происходит согласование для индивидуального insn, и только после
установления соответствовия insn шаблону распознавания образца. Операнды
insn могут быть получены из вектора " operands ".
4. " Шаблон вывода ": строка, которая говорит, как выводить
insns как код ассемблера. "%" в этой строке определяет, куда
подставлять значения операнда. * См.: Шаблон Вывода::.
Когда простой подстановки недостаточно, Вы можете делать вставку из
кода для получения вывода. * См.: Оператор Вывода::.
5. Опционально, вектор, содержащий значения атрибутов для insns,
- 80 -
соответствующих этому образцу. * См.: Атрибуты Insn::.
5.2. Пример " define_insn "
Вот фактический пример образца команды для
68000/68020.
(define_insn "tstsi"
[(set (cc0)
(Match_operand: SI 0 "general_operand" "rm"))]
""
" *
{ if (TARGET_68020 || ! ADDRESS_REG_P (operands [0]))
return \"tstl %0\";
return \"cmpl *0, %0\"; }")
Это - команда, которая устанавливает условные коды, основанные на
значении общего операнда. Оно не имеет условия, так что любой insn, чье RTL
описание имеет указанную форму, может быть обработан согласно этому
образцу. Имя "tstsi" означает " test a 'SImode' value" и сообщает
проходу генерации RTL, что, когда необходимо проверить такое значение,
insn для того, чтобы сделать это, можно создать, используя этот образец.
Строка управления выводом - C код, который выбирает
шаблон вывода для возврата, основываясь на виде операнда и специфическом
типе процессора, для которого генерируется код.
` "rm" ' является ограничением операнда. Его значение объясняется ниже.
5.4. RTL Шаблоны
RTL шаблон используется, чтобы определить, какие insns
соответствуют конкретному образцу и как найти их операнды.
Для названных образцов, RTL шаблон также показывает, как создать insn из
данных операндов.
- 81 -
Конструкция включает замену определенных операндов в копию шаблона.
Соответствие включает определение значений, которые служат как операнды в
согласовываемом insn. Оба из этих действий управляются специальными типами
выражения, которые руководят соответствием и заменой операндов.
`(match_operand:M N PREDICATE CONSTRAINT)'
Это выражение - placeholder для операнда insn с номером N. При
построении insn в этом месте операнд с номером N будет заменяться.
При поиске соответствия insn-у, что угодно, появляющееся в этой позиции
insn, будет приниматься как операнд номер N; но оно должно удовлетворять
PREDICATE, или этот образец команды не будет соответствовать вообще.
Номера операндов должны быть выбраны, последовательно считая от нуля
для каждого образца команды. Для каждого номера операнда в образце может
иметься только одно выражение `match_operand'. Обычно операнды
пронумерованы в порядке появления в выражениях `match_operand'.
PREDICATE - строка, которая является именем С-функции с двумя аргументами:
выражение и машинный режим. В течение соответствия, функция будет
вызываться с мнимым операндом как параметр выражения, и M как параметр
режима (если M не определен, будет использоваться режим 'VOIDmode',
который обычно заставляет предикат принимать любой режим). Если
возвращается ноль, то эти образцы команды не подходят для соответствия.
PREDICATE может быть пустой строкой, это означает, что над операндом не
надо выполнять никакого теста, и корректным является любое выражение,
стоящее в этой позиции.
Почти всегда PREDICATE отклонит режимы, отличные от M, но не совсем.
Например, предикат 'address_operand' использует M как режим памяти,
касательно которой адрес должен быть корректен. Многие предикаты
принимают 'const_int', даже если их режим - 'VOIDmode' .
CONSTRAINT управляет перезагрузкой и выбором наилучшего класса
регистров для хранения значений. ( Подробнее это будет
объяснено ниже (см. ограничения::.) ).
Людям часто не ясны различия между ограничениями и предикатами.
- 82 -
Предикат помогает решить, соответствует ли данный insn образцу.
Ограничения не играют никакой роли в этом решении, но они управляют
дальнейшими решениями в случае, когда insn соответствует.
На CISC машинах, наиболее распространенный PREDICATE - это
'general_operand'. Эта функция проверяет является ли мнимый операнд
константой, регистром или ячейкой памяти, и корректен ли операнд для
режима M.
Для операнда, который должен быть регистром, PREDICATE должен быть
'register_operand'. Возможно так же использование предиката
'general_operand', так как проход перезагрузки копирует любые операнды
не-регистры через регистры, но это заставляло бы делать GNU CC
дополнительную работу, и предотвращало бы инвариантные операнды
(типа константы) от удаления из циклов, и помешало бы регистру
allocator выполнять работу наилучшем способом. На RISC машинах, обычно
наиболее эффективно позволить PREDICATE пропускать только те
объекты, которые позволяют ограничения.
Для операнда, который должен быть константой, Вы должны убедиться, что
либо Вы использовали 'immediate_operand' для PREDICATE, либо сделали
дополнительное условие образца команды требующее константу, или и то и
другое. Одних ограничений может быть недостаточно! Если ограничения
разрешают только константы, а предикат позволяет что-то еще, то при
возникновении такой ситуации транслятор разрушится.
`(match_scratch:M N CONSTRAINT)'
Это выражение - также placeholder для операнда с номером N и указывает,
что операнд должен быть выражением 'scratch' или `reg' .
При соответствии образцов, это эквивалентно
(match_operand:M N "scratch_operand" PRED)
Но, при генерации RTL, это дает выражение ( 'scratch': M ).
Если последние несколько выражения в 'parallel' являются выражениями
`clobber', чьи операнды являются либо аппаратным регистром либо
- 83 -
'match_scratch', комбайнер может добавлять или удалять их когда
необходимо.
`(match_dup N)'
Это выражение - также placeholder для операнда с номером N, оно
используется, когда операнд должен появиться в insn больше чем один раз.
При конструировании, 'match_dup' действует точно так же как
'match_operand': в создаваемом insn заменяется операнд. Но в
соответствии, 'match_dup' ведет себя по-другому - он принимает, что
операнд номер N уже был определен ранее с помощью 'match_operand' в
шаблоне распознавания, и он соответствует только идентично выглядящему
выражению.
`(match_operator:M N PREDICATE [OPERANDS...])'
Этот образец - разновидность placeholder для переменного кода
выражения RTL.
При построении insn, это заменяется на RTL выражение, чей код выражения
взят из такового операнда N, и чьи операнды созданы из образцов
OPERANDS.
При поиске соответствия, это соответствует выражению, если функция
PREDICATE возвращает не ноль на это выражение *и* образцы
OPERANDS соответствуют операндам выражения.
Предположим, что функция 'commutative_operator' определена следующим
образом: соответствует любому выражению, чей оператор - один
из коммутативных арифметических операторов RTL и чей режим - MODE:
int
commutative_operator (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code = GET_CODE (x);
if (GET_MODE (x) != mode)
- 84 -
return 0;
return (GET_RTX_CLASS (code) == 'c'
|| code == EQ || code == NE);
}
Тогда следующий образец будет соответствовать любому выражению RTL,
состоящему из коммутативного оператора, примененного к двум общим
операндам:
(match_operator:SI 3 "commutative_operator"
[(match_operand:SI 1 "general_operand" "g")
(match_operand:SI 2 "general_operand" "g")])
Здесь вектор `[OPERANDS...]' содержат два образца, потому что все
выражения, которым нужно найти соответствия, содержат два операнда.
Когда этот образец соответствует, два операнда коммутативного оператора
регистрируются как операнды 1 и 2 из insn. (Это выполнено двумя
'match_operand'-ами.) Операнд 3 из insn будет полностью
коммутативным выражением: используйте 'GET_CODE (операнды [3])
чтобы видеть, какой коммутативный оператор был использован.
Тип M 'match_operator'-а работает аналогично типу 'match_operand'-а:
он передается как второй параметр к функции предиката, и эта функция
полностью ответственна за решение, имеет ли выражение, которое будет
согласовано, этот тип.
При построении insn, третий параметр gen-функции определит операцию
(то есть код выражения) для выражения, которое будет сделано. Это
должно быть выражение RTL, чей код выражения скопирован в новое
выражение и чьи операнды - параметры 1 и 2 gen-функции. Подвыражения
параметра 3 не используются; имеет значение только его код выражения.
Когда 'match_operator' используется в образце для соответствия insn,
то обычно лучше всего, если номер операнда 'match_operator'
выше чем номера фактических операндов insn. Это улучшает распределение
регистров, потому что allocator регистров часто рассматривает операнды
1 и 2 из insns, чтобы узнать, может ли он связать эти регистры.
- 85 -
Не существует способа определить ограничения в 'match_operator' .
Операнд insn, который соответствует match_operator' никогда
не имеет никаких ограничений, потому что он никогда не перезагружается
как целое. Однако, если части его OPERANDS соответствуют 'match_operand'
образцам, эти части могут иметь ограничения на них самих.
`(match_op_dup:M N[OPERANDS...])'
Подобен 'match_dup', за исключением того, что применяется к операторам
вместо операндов. При построении insn, операнд номер N на этом месте
будет заменяться. Но в соответствии, 'match_op_dup' ведет себя
по-другому. Он принимает, что операнд номер N уже был определен
'match_operator'-ом, появляющимся в шаблоне распознавания ранее, и он
соответствует только идентично выглядящему выражению.
`(match_parallel N PREDICATE [SUBPAT...])'
Этот образец - placeholder для insn, который состоит из выражения
`parallel' с переменным числом элементов. Это выражение может
появиться только на верхнем уровне insn образца.
При построении insn, операнд номер N будет заменяться на этом месте.
При соответствии insn, это соответствует, если тело insn - выражение
`parallel' с по крайней мере таким количеством элементов, как вектор
выражений SUBPAT в 'match_parallel' , если каждый SUBPAT соответствует
соответствующему элементу 'parallel', *и* функция PREDICATE возвращает
не ноль на 'parallel', который является телом insn. За корректность
элементов 'parallel', которые не указаны в `match_parallel', несет
ответственность предикат.
Обычно `match_parallel' используют, чтобы сравнивать его с
многочисленными выражениями чтения и записи, которые могут содержать
различное число элементов в 'parallel'. Например:
(define_insn ""
[(match_parallel 0 "load_multiple_operation"
[(set (match_operand:SI 1 "gpc_reg_operand" "=r")
(match_operand:SI 2 "memory_operand" "m"))
- 86 -
(use (reg:SI 179))
(clobber (reg:SI 179))])]
""
"loadm 0,0,%1,%2")
Этот пример взят из 'a29k.md'. Функция 'load_multiple_operations'
определена в 'a29k.c' и проверяет, что последующие элементы в
`parallel' те же, что и `set' в образце, за исключением того, что они
ссылаются на последующие регистры и ячейки памяти.
Insn, который соответствует этому образцу, может выглядеть например так:
(parallel
[(set (reg:SI 20) (mem:SI (reg:SI 100)))
(use (reg:SI 179))
(clobber (reg:SI 179))
(set (reg:SI 21)
(mem:SI (plus:SI (reg:SI 100)
(const_int 4))))
(set (reg:SI 22)
(mem:SI (plus:SI (reg:SI 100)
(const_int 8))))])
`(match_par_dup N [SUBPAT...])'
Подобно 'match_op_dup', но для 'match_parallel' вместо 'match_operator'.
`(address (match_operand:M N "address_operand" ""))'
Этот комплекс выражений - placeholder для операнда номер N в инструкции
"загрузки адреса": операнд, который определяет ячейку памяти
обычным способом, но для которого фактическое значение операнда
есть адрес ячейки, а не ее содержание.
Выражения `address' никогда не появляются в RTL коде, только в машинных
описаниях. И они используются только в машинных описаниях, которые не
используют особенности ограничения операнда. Когда ограничения операнда
используются, символ 'p' в ограничении служит этой цели.
M - тип *адресуемой ячейки памяти*, а не тип адреса. Этот тип
- 87 -
всегда один и тот же на данной целевой машине ( 'Pmode' , который
обычно является 'SImode'), так что не имеет смысла это упоминать;
таким образом, в выражении 'address' не написан никакой тип.
Если однажды будет добавлена поддержка машин, в которых адреса
объектов различного вида появляются по-разному или используются
по-разному (типа PDP-10), различные форматы, возможно, будут нуждаться
в различных типах, и эти типы должны быть упомянуты в выражении
'address'.
5.5. Шаблоны вывода и замена операндов
'Шаблон вывода' - это строка, которая определяет, как выводить
ассемблерный код для образца команды. Почти весь шаблон - фиксированная
строка, которая выводится буквально. Символ '%' используется, чтобы
определить место замены операндом; он может также использоваться, чтобы
обозначать места, где различные варианты ассемблера требуют различного
синтаксиса.
В самом простом случае, '%', а за ним число N указывает, что надо вывести
операнд N в этом месте.
'%' после которого стоит символ и цифра говорит, что надо вывести операнд
альтернативным способом. Четыре символа имеют стандартные, встроенные
значения, описанные ниже. Машинная макрокоманда описания 'PRINT_OPERAND'
может определять дополнительные символы с нестандартными значениями.
'%cDIGIT' может использоваться для замены операнда, который является
постоянным значением без синтаксиса, который обычно указывает
непосредственный операнд.
'%nDIGIT' тоже, что и '%cDIGIT' , но значение константы перед печатью
заменяется на минус ее.
'%aDIGIT ' может использоваться, чтобы заменить операнд, так как если бы
это была ссылка на ячейку памяти, фактическим операндом, обрабатываемым
как адрес. Это может быть полезно, когда выводится инструкция "загрузить
адрес", потому что часто синтаксис ассемблера для такой команды требует,
- 88 -
чтобы Вы писали операнд, как будто это была бы ячейка память.
'%lDIGIT ' используется, чтобы заменить ' label_ref ' на команду перехода.
'%=' выводит уникальный номер для каждой команды во всей трансляции. Это
полезно для создания локальных меток, которые нужно упомянуть в шаблоне
больше чем один раз, генерирующем многократные команды ассемблера.
'%' за которым следует символ пунктуации определяет замену, не использующую
операнд. Стандартным является только один случай : ' %% ' - выводит в код
ассемблера '%' . Другие, нестандартные случаи могут быть определены в
макрокоманде 'PRINT_OPERAND' . Вы также должны определить, какие символы
пунктуации корректны, в макрокоманде 'PRINT_OPERAND_PUNCT_VALID_P' .
Шаблон может генерировать несколько команд ассемблера. Пишите текст для
таких команд, разделяя их ' \; ' .
Когда RTL содержит два операнда, которые из-за ограничения
должны соответствовать друг другу, шаблон вывода должен обратиться только к
тому операнду, номер которого меньше. Соответствующие операнды не всегда
идентичны, и остальная часть транслятора работает так, чтобы поместить
соответствующее выражение RTL для печати в операнд с меньшим номером.
Используя нестандартные символы или пунктуацию после '%', нужно
проводить различие между различными языками ассемблеров для той же самой
машины; например, синтаксисом Motorola и синтаксисом MIT для 68000.
Синтаксис Motorola требует точек в большинстве имен кода операции, в то время
как MIT синтаксис не требует этого. Например, код операции 'movel' в MIT
синтаксисе - 'move.l' в Motorola синтаксисе. Для обоих видов синтаксиса вывода
используется один и тот же файл образцов, но в каждом месте, где Motorola
синтаксис хочет точку, используется символьная последовательность '%'.
Макрокоманда PRINT_OPERAND для Motorola синтаксиса определяет
последовательность, чтобы выводить точку; макрокоманда для MIT синтаксиса
определяет, что не надо ничего выводить.
Как специальный случай, шаблон, состоящий из одиночного символа '*' дает
инструкцию транслятору, чтобы сначала разбить insn, а затем вывести полученные
команды по отдельности. Это помогает устранять избыточность в шаблонах вывода.
- 89 -
Если Вы имеете 'define_insn' который должен создать несколько команд
ассемблера, и имеется уже определенное соответствие - 'define_split' , то Вы
можете просто использовать '*' как шаблон вывода, вместо того, чтобы
создавать шаблон вывода, генерящий несколько ассемблерных команд.
Если 'ASSEMBLER_DIALECT' определен, Вы можете использовать в шаблонах
конструкции типа '{option0 | option1 | option2} '. Они описывают
множество вариантов синтаксиса языка ассемблера. см. Вывод команд
5.6. Операторы С для вывода ассемблера
Часто одиночная фиксированная строка шаблона не может производить во всех
случаях, подходящих под одиночный образец команды, правильный и эффективный
код ассемблера. Например, коды операции могут зависеть от вида операндов; или
некоторые неудачные комбинации операндов могут требовать дополнительных
машинных команд.
Если строка управления вывода начинается с '@' , то это - ряд шаблонов,
каждый на отдельной строке. (Пустые строки, ведущие пробелы и знаки табуляции
игнорируются.) Шаблоны соответствуют вариантам ограничения образца
(см. Множественные альтернативы). Например, если целевая машина имеет
двухадресную команду сложения 'addr' для сложения в регистр и другую
'addm' , чтобы добавить регистр к памяти, то Вы можете использовать этот
образец:
(define_insn "addsi3"
[(set (match_operand:SI 0 "general_operand" "=r,m")
(plus:SI (match_operand:SI 1 "general_operand" "0,0")
(match_operand:SI 2 "general_operand" "g,r")))]
""
"@
addr %2,%0
addm %2,%0")
Если строка управления вывода начинается с '*' , то далее идет не шаблон
вывода, а часть программы C, которая должна вычислить шаблон. Она должна
использовать оператор 'return' , чтобы возвратить строку шаблона, которую
- 90 -
Вы хотите. Большинство таких шаблонов использует строчные литералы C,
которые должны быть разграничены символами двойных кавычек. Перед каждым
символом двойных кавычек надо ставить '\' .
Операнды могут быть найдены в массиве 'operands' ,тип данных которого в С- '
rtx [] ' .
Общепринято выбирать различные способы генерирования ассемблерного кода,
базирующихся на том, находится ли непосредственный операнд внутри некоторого
диапазона. Будьте внимательным при выполнении этого, потому что результат
'INTVAL' на главной машине является целым числом. Если главная машина имеет
большее количество битов в 'int' чем целевая машина имеет в типе, в котором
константа будет использоваться, то некоторые из битов, которые Вы получаете
из 'INTVAL', будут лишними. Для правильных результатов, Вы должны
тщательно игнорировать значения этих битов.
Возможно вывести команду ассемблера а затем продолжать выводить или
вычислять остальные команды, используя подпрограмму 'output_asm_insn'.
Она получает два параметра: строка шаблона и вектор операндов. Вектор может
быть 'operands', или это может быть другой массив 'rtx' который Вы объявляете
локально и инициализируете самостоятельно.
Когда образец insn имеет несколько вариантов ограничений, часто появление
ассемблерного кода определяется в основном тем, какой из вариантов был
согласован. Когда это - так, C код может проверять переменную
'which_alternative ', являющуюся порядковым номером варианта, который был
согласован первым (0 для первого, 1 для второго варианта, и т.д.).
Например, предположим, что имеются два кода операции для сохранения нуля,
'clrreg ' для регистров и 'clrmem' для ячеек памяти. Ниже приведен пример,
как лбразец может использовать 'which_alternative ' для того, чтобы выбрать
между ними:
(define_insn ""
[(set (match_operand:SI 0 "general_operand" "=r,m")
(const_int 0))]
""
"*
- 91 -
return (which_alternative == 0
? \"clrreg %0\" : \"clrmem %0\");
")
В вышеприведенном примере ассемблерный код *полностью* определялся
выбранной альтернативой, но он мог быть также определен через
строку управления выводом начинающуюся с '@' следующим образом:
(define_insn ""
[(set (match_operand:SI 0 "general_operand" "=r,m")
(const_int 0))]
""
"@
clrreg %0
clrmem %0")
5.7. Ограничения операндов
Каждый 'match_operand' в образце команды может определять ограничение на
тип операндов. Ограничения могут указывать, может ли операнд быть
в регистре, и в каких видах регистров; может ли операнд быть ячейкой памяти,
и каких видов адрес; может ли операнд быть непосредственной константой, и
какие возможные значения он может иметь. Ограничения могут также требовать
соответствия между двумя операндами.
5.7.1. Простые Ограничения
Самый простой вид ограничения - строка, заполненная символами, каждый из
которых описывает один вид разрешенного операнда. Имеются следующие
разрешенные символы:
`m'
Разрешается операнд памяти с любым видом адреса, который машина вообще
поддерживает.
`o'
- 92 -
Разрешается операнд памяти только, если адрес - "смещаемый". Это
означает, что добавление небольшого целого числа (фактически, ширины
операнда в байтах, как определено его типом) может быть
добавлено к адресу, и результат - также корректный адрес памяти.
Например, адрес, который является постоянным - смещаемый; поэтому
адрес, который является суммой регистра и константы тоже смещаемый
(если только небольшое увеличение константы оставляет адрес внутри
диапазона, поддерживаемого машиной); но самоувеличивающийся или
самоуменьшающийся адрес - не смещаемый. Более сложные
косвенные/индексированные адреса могут быть или не быть смещаемыми в
зависимости от других способов адресации, которые
поддерживает машина.
Обратите внимание на то, что в операнде вывода, который может быть
согласован с другим операндом, символ ограничения 'o' корректен только
когда сопровождает оба операнда : '<' (если целевая машина имеет
увеличение адреса) и '>' (если целевая машина имеет уменьшение адреса).
`V'
Не смещаемый операнд памяти. Другими словами, любой операнд,
удовлетворяющий 'm' ограничению, но не 'o' ограничению.
`<'
Разрешаются операнды памяти с автоматической декрементацией адреса
(либо преддекрементацией либо последекрементацией).
`>'
Разрешаются операнды памяти с автоматической инкрементацией адреса (либо
прединкрементацией либо постинкрементацией).
`r'
Разрешается операнд регистра, при условии, что он находится в общем
регистре.
`d', `a', `f', ...
Другие символы могут быть определены машинно-зависимым способом вместо
- 93 -
конкретных классов регистров. 'D' , 'a' и 'f' определены на
68000/68020, для регистров данных, регистров адреса и регистров
с плавающей точкой соответственно.
`i'
Разрешается непосредственный целочисленный операнд (с постоянным
значением). Сюда так же включаются символьные константы, чьи значения
будут известны только во время трансляции.
`n'
Разрешается непосредственный целочисленный операнд с известным числовым
значением . Большинство систем не поддерживают константы, созданные во
время трансляции, для операндов, меньших чем слово. Ограничения
для этих операндов должны использовать скорее 'n' чем 'i' .
`I', `J', `K', ... `P'
Другие символы в диапазоне от 'I' до 'P' могут быть определены
машинно-зависимым способом для разрешения непосредственных целочисленных
операндов с явными целочисленными значениями в определенных диапазонах.
Например, на 68000, 'I' определен для диапазона значений от 1 до 8.
Это диапазон, разрешенный для размера сдвига в командах сдвига.
`E'
Разрешен непосредственный операнд с плавающей точкой (код выражения
'const_double' ), но только, если формат типа с плавающей точкой
на целевой машине совпадает с соответствующим форматом на главной
машине.
`F'
Разрешается непосредственный операнд с плавающей точкой (код выражения
'const_double').
`G', `H'
'G' и ' H' могут быть определены машинно-зависимым способом, чтобы
разрешать непосредственные вещественные операнды в конкретных диапазонах
значений.
- 94 -
`s'
Разрешается непосредственный целочисленный операнд, чье значение не
является явно заданным целым число.
Это может показаться странным; если insn разрешает постоянный операнд
со значением, не известным во времени компиляции, то конечно же он должен
позволять и любое известное значение. Так почему используется 's' вместо
'i'. Иногда это позволяет сгенерировать лучший код.
Например, на 68000 в fullword команде возможно использовать
непосредственный операнд; но если непосредственное значение - между
-128 и 127, лучший код получится, если загрузить значение в регистр и
использовать регистр. Это происходит из-за того, что загрузка в регистр
может быть выполнена с помощью команды 'moveq'. Мы предусмотрели это,
определив, что символ 'K' означает любое целое число вне диапазона
от -128 до 127 и тогда записав 'Ks' в ограничения операнда.
`g'
Разрешается любой регистр, память или непосредственный целочисленный
операнд, кроме регистров, которые не являются общими.
`X'
Разрешается любой операнд, даже если он не удовлетворяет
'general_operand'. Обычно это используется в ограничении 'match_scratch '
когда некоторые варианты фактически не будут требовать рабочего регистра.
`0', `1', `2', ... `9'
Разрешается операнд, который соответствует определенному номеру операнда.
Если цифра используется вместе с символами внутри того же самого
варианта, то цифра должна стоять последней.
Это называется 'соответствие ограничения' и это действительно означает
то, что ассемблер имеет только один операнд, который выполняет две роли,
рассматриваемые отдельными в RTL insn. Например, insn сложения имеет два
входных операнда и один операнд вывода в RTL, но на большинстве CISC
машин команда сложения в действительности имеет только два операнда,
один из них операнд ввода-вывода:
- 95 -
addl #35,r12
В этих обстоятельствах используется соответствующие ограничения . Более
точно, два операнда, соответствующих друг другу, должны включить один
только входной операнд и один только выходной операнд. Более того,
цифра должна быть меньше, чем номер операнда, который использует ее
в ограничении.
То, что операнды соответствуют друг другу, в конкретном случае
означает, что они идентично выглядящие выражения RTL. Но в нескольких
специальных случаях разрешаются определенные виды различий. Например,
'*x' как входной операнд будет соответствовать '*x++' как операнд
вывода. Для правильных результатов в таких случаях, шаблон вывода должен
всегда использовать номер операнда вывода при печати операнда.
`p'
Разрешается операнд, который является корректным адресом памяти.
Это нужно для инструкций "load address" и "push address".
'P' в ограничении должен сопрвождаться 'address_operand' как предикат в
'match_operand'. Этот предикат интерпретирует тип, определенный в
'match_operand' как тип ячейки памяти, для которой адрес был бы
корректным.
`Q', `R', `S', ... `U'
Символы в диапазоне от 'Q' до 'U' могут быть определены
машинно-зависимым способом вместо произвольных типов операнда.
Машинной макрокоманде описания 'EXTRA_CONSTRAINT' передается операнд
как первый параметр и символ ограничения как второй операнд.
Типичное использование для этого - различение некоторых типы ссылок
памяти, которые воздействуют на другие insn операнды.
Не определяйте эти символы ограничения, чтобы принять ссылки регистра
('reg'); проход перезагрузки не ожидает этого и не обработает это
правильно.
- 96 -
Чтобы иметь корректный код ассемблера, каждый операнд должен удовлетворить
своим ограничениям. Но даже если это не так, то это не предотвращает действия
образца. Взамен, он указывает транслятору изменить код так, чтобы
ограничение было удовлетворено. Обычно это выполняется копированием операнда
в регистр.
Сравните два следующих образца:
(define_insn ""
[(set (match_operand:SI 0 "general_operand" "=r")
(plus:SI (match_dup 0)
(match_operand:SI 1 "general_operand" "r")))]
""
"...")
который имеет два операнда, один из которых должен появиться в двух местах, и
(define_insn ""
[(set (match_operand:SI 0 "general_operand" "=r")
(plus:SI (match_operand:SI 1 "general_operand" "0")
(match_operand:SI 2 "general_operand" "r")))]
""
"...")
который имеет три операнда, два из которых по требованию ограничения должны
быть идентичны. Если мы рассматриваем insn вида
(insn N PREV NEXT
(set (reg:SI 3)
(plus:SI (reg:SI 6) (reg:SI 109)))
...)
первый образец не применился бы вообще, потому что этот insn не содержит два
идентичных подвыражения в нужном месте. Образец сказал бы "Это не
похоже на команду сложения; попробуйте другие образцы." Второй образец
сказал бы "Да, это команда сложения, но с ней что-то не так." Он направил
- 97 -
бы проход перезагрузки транслятора, чтобы сгенерировать дополнительные insns,
делающие ограничения истинными. Результаты могли бы выглядеть следующим
образом:
(insn N2 PREV N
(set (reg:SI 3) (reg:SI 6))
...)
(insn N N2 NEXT
(set (reg:SI 3)
(plus:SI (reg:SI 3) (reg:SI 109)))
...)
Это для того, чтобы удостовериться что каждый операнд, в каждом образце,
имеет ограничения, которые могут обрабатывать любое выражение RTL, которое
могло бы присутствовать для того операнда. (Когда используются множественные
варианты, каждый образец для каждой возможной комбинации выражений операнда
должен иметь по крайней мере один вариант, который может обрабатывать ту
комбинацию операндов.) Ограничения не должны *позволять* любой возможный
операнд, но они должны по крайней мере указать путь на перезагрузку любого
возможного операнда так, чтобы он удовлетворил ограничениям.
* Если ограничение принимает любые операнды, которые разрешает предикат,
то не будет никаких проблем: перезарядка для этого операнда никогда не
потребуется.
Например, операнд, чьи ограничения разрешают все за исключением
регистраторов, безопасно обеспечит свой предикат отклонением
регистров.
Операнд, чьи вводы предиката только постоянные значения, безопасно
обеспечит ограничения, включая символ 'i' . Если принято любое
возможное постоянное значение, то ничего меньшее 'i' не пропустится;
если предикат более выборочный, то ограничения могут также быть
более выборочные.
* Любое выражение операнда может быть перезаряжено, с помощью копирования
в регистр. Так, если ограничения операнда позволяют некоторый регистр,
- 98 -
то это заведомо безопасно. Требуется разрешить все классы регистраторов;
транслятор знает, как копировать регистр в другой регистр
соответствующего класса, чтобы делать корректные команды.
* Несмещаемая ячейка памяти может быть перезаряжена, копируя адрес в
регистр. Так, если ограничение использует символ 'o', то все ячейки
будут перезаряжены.
* Постоянный операнд может быть перезагружен с помощью выделения
места в памяти, чтобы держать его как преинициализированные данные.
Поэтому вместо константы может быть использована ссылка на память.
Так, что если ограничения содержит символ `o' или `m', то константные
операнды так же проходят.
* Если ограничение разрешает константу, и псевдорегистр, используемый
в insn, который может быть расположен не в аппаратном регистре и
поэтому эквивалентен константе, то регистр будет заменен на константу.
Если предикат не разрешает константу, а insn по некоторым причинам
повторно распознан, транслятор разрушится. Таким образом предикат
должен всегда распознавать любые объекты, позволенные ограничением.
Если предикат операнда пропускает регистры, а ограничение не
разрешает их, то это может привести к аварийному отказу транслятора. Когда
этот операнд регистр, проход перезагрузки будет stymied, потому что он не
знает, как временно копировать регистр в память.
5.7.2. Многократные Альтернативные Ограничения
Иногда одиночная команда имеет много альтернативных наборов возможных
операндов. Например, на 68000, команда логического or может объединять
регистр и непосредственное значение памяти, так же она может объединять
операнд любого вида в регистр; но она не может объединять одну ячейку
памяти с другой.
Эти ограничения представляются несколькими вариантами. Вариант может быть
описан рядом символов для каждого операнда. Полное ограничение для операнда
сделано из символов для этого операнда из первого варианта, запятой, символы
- 99 -
для этого операнда из варианта второго, запятой, и так далее до последнего
варианта. Далее показывается как это делается для логического или с полными
словами на 68000:
(define_insn "iorsi3"
[(set (match_operand:SI 0 "general_operand" "=m,d")
(ior:SI (match_operand:SI 1 "general_operand" "%0,0")
(match_operand:SI 2 "general_operand" "dKs,dmKs")))]
...)
Первый вариант имеет 'm' (память) для операнда 0, '0' для операнда 1
(его значение должно соответствовать операнду 0), и 'dKs' для
операнда 2. Другой вариант имеет 'd' (регистр данных) для операнда 0,
'0' для операнда 1, и 'dmKs' для операнда 2. '=' и '%' в ограничениях
обращаются ко всем вариантам; их значение объясняется в следующем разделе
(см. Предпочтения классов).
Если все операнды удовлетворяют любому варианту, команда корректна.
Иначе, для каждого варианта, транслятор рассчитывает, сколько команд
должны быть добавлены, чтобы скопировать операнды таким образом, чтобы
применился этот вариант. Будет выбран вариант, требующий наименьшего
копирования. Если двум вариантам требуется одинаковое количество
копирования, будет выбран первый. Эти выборы могут быть изменены с
помощью символов '?'и '!':
`?'
Немного понижает приоритет варианта, в котором появляется. Работает
в случае, когда не один из вариантов точно не выбран. Транслятор
расценивает такой вариант как более приоритетный.
`!'
Понижает приоритет этого варианта до минимума. Транслятор все еще
может использовать этот вариант, если он может подойти без перезагрузки.
Но если перезагрузка необходима, то обязательно будет выбран другой
вариант.
Если образец insn имеет множество вариантов ограничений, то часто появление
ассемблерного кода в основном определяется тем, какой вариант был выбран.
- 100 -
Когда это - так, C код для записи кода ассемблера может использовать
переменную 'which_alternative' , которой является порядковым номером
варианта, который был фактически удовлетворен (0 для первого, 1 для
варианта второго, и т.д.). см. Оператор вывода
5.7.3. Предпочтения классов регистров
Ограничения операнда служат для других целей: они дают возможность
транслятору решить, как лучше всего использовать аппаратные и псевдо регистры.
Транслятор исследует ограничения, которые обращаются к insns, использующие
псевдо регистр, ища машинно-зависимые символы типа 'd' и 'a' которые
определяют классы регистраторов. Псевдо регистр помещен в любой класс
становит наиболее голосующим. Символы ограничения 'g' и 'r' также голосуют:
они голосуют в пользу общего регистра. В машинном описании говорится, какие
регистры являются общими.
Конечно, на некоторых машинах все регистры эквивалентны, и нет никаких
классов регистров. И тогда все что описано ниже не нужно.
5.7.4. Символы модификатора ограничения
Далее идут символы модификатора ограничения.
`='
Означает, что этот операнд в этой команде только для чтения: предыдущее
значение отбрасывается и заменяется данными результата.
`+'
Означает, что этот операнд инструкция может как читать так и писать.
Когда транслятор устанавливает операнды, чтобы удовлетворить ограничению,
требуется знать, какие операнды являются вводами к команде а какие
являются выводами из нее. '=' идентифицирует вывод; '+' идентифицирует
операнд, который является, и вводом и выводом; все другие операнды
только входные.
`&'
- 101 -
Означает (в отличии от других случаев) что этот операнд записан прежде,
чем команда завершена, используя входные операнды. Следовательно, этот
операнд не может находиться в регистре, используемом как входной операнд
или как часть любого адреса памяти.
'&' применяется только к варианту, в котором это написано. В
ограничениях с многократными вариантами, иногда один вариант требует '&'
в то время как другие нет. См. например, 'movdf' insn из 68000.
'&' не устраняет потребность писать '=' .
`%'
Объявляет, что команда будет коммутативна для этого и следующего
операндов. Это означает, что транслятор может обмениваться двумя
операндами, если это самый дешевый способ заставить все операнды
удовлетворять ограничению. Это часто используется в образцах для
команды сложения, которая действительно имеют только два операнда:
результат должен войти в один из параметров. Здесь, например, описано
как определена инструкция сложения полуслов в 68000:
(define_insn "addhi3"
[(set (match_operand:HI 0 "general_operand" "=m,r")
(plus:HI (match_operand:HI 1 "general_operand" "%0,0")
(match_operand:HI 2 "general_operand" "di,g")))]
...)
`#'
Говорит, что весь последующие символы, вплоть до следующей запятой,
как ограничение должны игнорироваться. Эти символы значимы только для
выбор предпочтительного регистра.
`*'
Говорит, что следующий символ при выборе предпочтительного регистра
должен игнорироваться. '*' не оказывает никакого эффекта на значение
ограничения, перезарядку.
Далее идет пример: 68000 имеет команду для знакового распространения
половины слова в регистр данных, и возможно также знаковое
- 102 -
распространение с помощью копирования в регистр адреса. В то время как
любой вид регистра приемлем, ограничения на адресата регистра адреса
менее строги, так что самое лучшее, если распределение регистра
заставляет адрес регистрировать цель. Следовательно, '*' используется
так, чтобы символ ограничения 'd' (для регистра данных)
игнорировался при вычислении предпочтения регистра.
(define_insn "extendhisi2"
[(set (match_operand:SI 0 "general_operand" "=*d,a")
(sign_extend:SI
(match_operand:HI 1 "general_operand" "0,g")))]
...)
5.7.5. Ограничения для Специфических Машин
По мере возможности, Вы должны пытаться использовать в параметрах 'asm'
ограничения общего назначения, т.к. это легче восприниматься другими
людьми. Если Вы этого не умеете, используйте символы ограничения, которые
имеют очень много разных конкретных значений для разных архитектур.
Наиболее часто используются: `m' и `r' (для памяти и регистров
общего назначения соответственно; см. Простые ограничения), и `I',
обычно символ, указывающий наиболее общий формат уже полуученого числа.
Для каждой архитектуры машины, файл 'config/MACHINE.h' определяет
дополнительные ограничения. Эти ограничения используются транслятором
непосредственно для генерации команд, также как для asm-операторов;
следовательно, некоторые из ограничений не особенно интересны для asm.
Ограничения определены через следующие макрокоманды:
`REG_CLASS_FROM_LETTER'
Ограничения типа регистра (обычно строчные буквы).
`CONST_OK_FOR_LETTER_P'
Непосредственные постоянные ограничения, для констант с неплавающей
точкой размером слова или меньше (обычно верхний регистр).
`CONST_DOUBLE_OK_FOR_LETTER_P'
- 103 -
Непосредственные постоянные ограничения, для всех констант с плавающей
точкой и для констант больших чем точность размера слова (обычно верхний
регистр).
`EXTRA_CONSTRAINT'
Специальные случаи регистров или памяти. Эта макрокоманда не
обязательна, и определена только для некоторых машин.
Проверка этих макроопределений в исподниках транслятора для вашей
машины - самый лучший способ убедиться, что Вы имеете правильные ограничения.
Как бы то ни было, здесь дано резюме машинно-зависимых ограничений, доступных
на некоторых специфических машинах.
*семейство ARM --`arm.h'*
`f'
регистр с плавающей точкой
'F'
Одна из следующих констант с плавающей точкой - 0.0, 0.5, 1.0, 2.0, 3.0,
4.0, 5.0 или 10.0
'G'
Константа с плавающей точкой, которая удовлетворила бы ограничению 'F'
если поменять у нее знак
'I'
Целое число, которое может использоваться как непосредственный операнд в
команде обработки данных. То есть целое число в диапазоне от 0 до 255
с вращаемым множителем 2
'J'
Целое число в диапазоне -4095 к 4095
'K'
Целое число, которое удовлетворяет ограничение 'I' если изменить знак
(одинарное дополнение)
'L'
- 104 -
Целое число, которое удовлетворило бы ограничению 'I' если поменять у него
знак (двойное дополнение)
'M'
Целое число в диапазоне 0 к 32
'Q'
Ячейка памяти, где точный адрес находится в одном регистре ( ``m''
является предпочтительной для `asm'-операнда )
'R'
Элемент constant pool
'S'
Символ в текстовом сегменте текущего(актуального) файла
*AMD 29000 family--`a29k.h'*
'l'
Локальный регистр 0
'B'
Регистр указателя байта ('BP')
'q'
'Q' регистр
'H'
Специальный регистр цели
`A'
Первый регистр сумматор
`a'
Другой сумматор
'f'
регистр с плавающей точкой
- 105 -
'I'
Константа большая чем 0, меньшая чем 0x100
'J'
Константа большая чем 0, меньшая чем 0x10000
'K'
Константа, чьи верхние 24 бита находятся на (1)
'L'
16 битная константа, чьи верхние 8 битов находятся на (1)
'M'
32 битная константа, чей верхние 16 битов находятся на (1)
'N'
32 битная отрицательная константа, которая помещается в 8 бит
'O'
Константа 0x80000000 или, на 29050, любые 32 разрядных константа, чей
первые 16 битов - 0.
'P'
16 битная отрицательная константа, которая помещается в 8 бит.
'G'
'H'
Константа с плавающей точкой (в asm-операторах, используйте взамен
машинно-независимые 'E' или 'F')
*IBM RS6000--`rs6000.h'*
'b'
Регистр основы Адреса
'F'
регистр с плавающей точкой
- 106 -
'H'
'MQ' , 'CTR' , или 'LINK' регистр
'Q'
'MQ' регистр
'C'
'CTR' регистр
'L'
'LINK' регистр
'X'
'CR' регистр номер 0 (регистр условия)
'Y'
'CR' регистр (регистр условия)
'I'
16-битная константа со знаком
'J'
Константа, чьи первые 16 бит - 0
'K'
Константа, чьи верхние 16 бит - 0
'L'
Константа подходящий как операнд маски
'M'
Константа больше чем 31
'N'
Точная мощность 2
'O'
Ноль
- 107 -
'P'
Константа, у которой если поменять знак, то получится
16 битная знаковая константа
'G'
Константа с плавающей точкой, которая может быть загружена в регистр с
помощи одной команды на слово
'Q'
Операнд Памяти, который является смещением из регистра ( 'm' является
предпочтительным для `asm'-операторов )
*Intel 386--`i386.h'*
'q'
'a' , 'b' , 'c' , или 'd' регистр
'A'
'a' , или 'd' регистр (для ints с 64 битами)
'f'
регистр с плавающей точкой
'T'
Первый регистр с плавающей точкой (начало стека)
'U'
Второй регистр с плавающей точкой
`a'
`a' регистр
`b'
`b' регистр
`c'
`c' регистр
- 108 -
`d'
`d' регистр
`D'
`di' регистр
`S'
`si' регистр
'I'
Константа в диапазоне 0 к 31 (для 32 разрядных сдвигов)
'J'
Константа в диапазоне 0 к 63 (для 64 разрядных сдвигов)
'K'
'0xff'
'L'
'0xffff'
'M'
0, 1, 2, или 3 (сдвигается c помощью 'lea' команды)
'G'
Стандартная 80387 константа с плавающей точкой
*Intel 960--`i960.h'*
'f'
регистр с плавающей точкой ( ' fp0 ' к ' fp3 ' )
'L'
Локальный регистр ( ' r0 ' к ' r15 ' )
'b'
- 109 -
Глобальный регистр ( ' g0 ' к ' g15 ' )
'd'
Любой - локальный или глобальный регистр
'I'
Целые числа от 0 до 31
'J'
0
'K'
Целые числа от -31 до 0
'G'
вещественный (с плавающей точкой) 0
'H'
вещественный (с плавающей точкой) 1
*MIPS--`mips.h'*
'D'
Целочисленный регистр общего назначения
'F'
регистр с плавающей точкой (если доступен)
'H'
'Hi' регистр
'L'
'Lo' регистр
'X'
'Hi' или 'Lo' регистр
'Y'
- 110 -
Целочисленный регистр общего назначения
'Z'
регистр состояния операций и команд с плавающей точкой
'I'
16 разрядная константа со знаком (для арифметических команд)
'J'
Ноль
'K'
Расширенная нулем 16-битная константа (для логических команд)
'L'
Константа у которой нижние 16 бит - 0 (может быть загружена ' lui ' )
'M'
32 битная константа, которая требует, две команды для загрузки
(константа которая - не `I', `K', или `L' )
'N'
Отрицательная 16-битная константа
'O'
Точная мощность 2
'P'
Положительная 16-битная константа
'G'
Вещественный (с плавающей точкой) 0
'Q'
Ячейка памяти памяти, которая может быть загружена более чем одной командой
( 'm' более предпочтительно для asm-операторов)
'R'
- 111 -
Ячейка памяти, которая может быть загружена одной командой
( 'm' более предпочтительно для asm-операторов)
'S'
Ячейка памяти во внешнем формате OSF/rose PIC
( 'm' более предпочтительно для asm-операторов)
*Motorola 680x0--`m68k.h'*
`a'
Регистр Адреса
'D'
Регистр Данных
'F'
68881 регистр числа с плавающей точкой, если доступен
'X'
Sun FPA (вещественный, с плавающей точкой) регистр, если доступен
'Y'
Первые 16 Sun FPA регистров, ксли доступны
`I'
Целое число в диапазоне от 1 до 8
`J'
16 битное знаковое число
'K'
Знаковое число, чья величина большая чем 0x80
'L'
Целое число в диапазоне от -8 до -1
'G'
Константа с плавающей точкой, не являющаяся 68881 константой
- 112 -
'H'
Константа с плавающей точкой, которая может использоваться Sun FPA
*SPARC--`sparc.h'*
'F'
регистр с плавающей точкой
`I'
Знаковая 13-битная константа
'J'
Ноль
'K'
32 битная константа с пустыми нижними 12 битами (константа, которая может
быть загружена командой 'sethi')
'G'
Вещественный (с плавающей точкой) 0
'H'
Знаковая 13 битная константа, знако-распространенная до 32 или 64 бит
'Q'
Ячейка памяти, которая может быть загружена одной командой
( 'm' более предпочтительно для asm-операторов)
'S'
Константа, или адрес памяти
'T'
Адрес памяти, выровненный к 8 байтовой границе
'U'
Просто регистр
- 113 -
5.7.6. Без ограничений
Некоторые машины настолько чисты, что ограничения на операнды
просто не нужны. Например, на Vax операнд, корректный в одном контексте,
будет так же корректен и в любом другом контексте. На таких машинах
каждый операнд должен иметь ограничение `g', исключая, разве что,
операнды инструкций загрузки адреса, которые написаны,
как будто они обращаются к ячейкам памяти, но фактический они
обращаются к адресу ячейки. Они имели бы ограничение 'p'.
Для таких машин, вместо того, чтобы записывать 'g' и 'p' для всех
ограничений, Вы можете писать описание с пустыми ограничениями.
Тогда Вы пишете `""' для ограничения в каждом 'match_operand' . Операнды
Адреса идентифицируются, записыванием выражения `address' рядом с
'match_operand', а не их ограничениями.
Когда машинное описание имеет только пустые ограничения, некоторые части
трансляции будут пропущены, что делает транслятор быстрым. Однако,
только очень малое число машин фактически не нуждаются в ограничениях;
все машинные описания теперь уже включают ограничения использования.
5.8. Стандартные Образцы Имен Для Генерации
Вот таблица имен команд, которые являются значимыми в
проход генерации RTL транслятора. Присваивание одного из этих имен
образцу команды сообщает проходу генерации RTL, что он может использовать
этот образец для выполнения некоторой задачи.
" movM "
Здесь M - имя машинного типа из двух символов, строчными буквами.
Этот образец команды перемещает данные с этим машинным типом из
операнда 1 в операнд 0. Например, " movsi " перемещает
данные длиной в слово.
Если операнд 0 - " subreg " с типом M регистра, чей собственный
тип более широкий, чем M, эффект этой команды состоит в сохранении
указанного значения в части регистра, соответствующей
- 114 -
типу M. Эффект на остальной части регистра не определен.
Этот класс образцов выделяется по нескольким причинам. Прежде всего
каждое из этих имен *должно* быть определено, потому что нет никакого
другого способа скопировать данные из одного места в другое.
Во-вторых, эти образцы используются не только в проходе генерации RTL.
Даже проход перезагрузки может генерировать перемещение insns, чтобы копировать значения
из слотов стека во временные регистры. Когда он делает это, один
из операндов - аппаратный регистр, а другой - операнд, который
может потребоваться перезагрузить в регистр.
Следовательно, когда задана такая пара операндов, образец должен
генерировать RTL, который не нуждается в перезагрузке и не нуждается во
временных регистрах - никаких регистрах, отличных от операндов. Например,
если Вы поддерживаете образец с " define_expand ", то в таком
случае " define_expand " не должен вызывать " force_reg " или любую
другую такую функцию, которая могла бы генерировать новые псевдорегистры.
Это требование существует даже для типов подслова на RISC машине,
на которой при выборке этих типов из памяти обычно требуется несколько
insns и несколько временные регистров. Посмотрите в " spur.md ", чтобы
увидеть как это требование может быть удовлетворено.
В течение перезагрузки ссылка памяти с недопустимым адресом может быть
передана как операнд. Такой адрес будет заменен на допустимый
адрес позже в проходе перезагрузки. В этом случае с адресом нельзя
делать ничего, за исключением использования его так, как это имеет место.
Если он копируется, он не заменяется на имеющий силу адрес. Не должно
делаться никакой попытки сделать такой адрес допустимым и нельзя вызывать
подпрограмму (типа " change_address "), которая будет это делать.
Обратите внимание, что " general_operand " потерпит неудачу, если
применить его к такому адресу.
Глобальная переменная " reload_in_progress " (которая должна быть явно
объявлена, если требуется) может использоваться, чтобы определить,
требуется ли такая специальная обработка.
- 115 -
Виды операндов, которые могут перезагружаться, зависят от остальной
части машинного описания, но обычно на RISC машинах они могут быть
только псевдорегистрами, который не получили аппаратных регистров,
в то время как на других машинах явные ссылки памяти иногда могут
перезагружаться.
Если рабочий регистр требуется, чтобы переместить объект в память или из
памяти, он может быть распределен с использованием " gen_reg_rtx " до
перезагрузки. Но это невозможно в течение и после перезагрузки. Если есть
случаи, нуждающиеся в рабочих регистрах после перезагрузки, Вы должны определить
" SECONDARY_INPUT_RELOAD_CLASs " и, возможно, также
" SECONDARY_OUTPUT_RELOAD_CLASs ", чтобы обнаружить их, и предоставить
образцы " reload_inM " или " reload_outM ", чтобы обработать их.
* См.: Классы Регистров::.
Ограничения на " moveM " должны позволять перемещать любой аппаратный
регистр в любой другой аппаратный регистр при условии, что
" HARD_REGNO_MODE_OK " позволеят тип M для обоих регистров, а
" REGISTER_MOVE_COST ", примененный к их классам, возвращает значение 2.
Обязательно поддерживать команды " moveM " с плавающей точкой
внутри и вне любых регистров, которые могут хранить значения с
фиксированной точкой, потому что объединения и структуры (которые имеют
типы " SImode " или " DImode ") могут быть в этих регистрах, и они
могут иметь поля с плавающей точкой.
Может также иметься потребность поддерживать команды " moveM "
с фиксированной точкой внутри и вне регистров с плавающей точкой.
К сожалению, я забыл, почему это было так, и я не знаю,
так ли это до сих пор. Если " HARD_REGNO_MODE_OK " отклоняет значения с
фиксированной точкой в регистрах с плавающей точкой, то ограничения
на команды " moveM " с фиксированной точкой должны быть предназначены,
чтобы избежать попытки перезагрузки в регистр с плавающей точкой.
" reload_inM "
" reload_outM "
Подобно " movM ", но используется, когда рабочий регистр требуется,
чтобы передвигать между операндом 0 и 1 операндом. Операнд 2 описывает рабочий
- 116 -
регистр. См. обсуждение макрокоманды " SECONDARY_RELOAD_CLASs "
в * См.: Классы Регистров:: ..
" movstrictm "
Подобно " movM " за исключением того, что, если операнд 0 - " subreg " с типом M
регистра, чей естественный тип более широкий, " movstrictM "
команда гарантированно не изменит регистра за исключением
части, которая принадлежит типу M.
" load_multiple "
Загружает несколько последовательных ячеек памяти в последовательные
регистры. Операнд 0 - первый из последовательных регистров,
операнд 1 - первая ячейка памяти, а операнд 2 -
константа: число последовательных регистров.
Определяйте эту команду, только если целевая машина действительно имеет такую
команду; не определяйте ее, если наиболее эффективный путь
загрузки последовательных регистров из памяти - загружать их по одному.
На некоторых машинах имеются ограничения на то, какая именно
последовательность регистров может быть сохранена в память, типа
ограничений на начальные или конечные номера регистров или на диапазон
допустимых количеств. Для таких машин используйте " define_expand " (* См.:
Расширитель определений::.) и делайте сбой образца, если ограничения
не выполнены.
Записывайте сгенерированный insn как " parallel " с элементами, являющимися
" set " одного регистра из соответствующей ячейки памяти (Вам могут
также потребоваться " use " или " clobber " элементы). Используйте " match_parallel ",
(* См.: RTL Шаблон::.) чтобы распознать insn. См. " a29k.md " и
" rs6000.md " для примеров использования этого образца insn.
" store_multiple "
Подобно " load_multiple ", но сохраняет несколько последовательных
регистров в последовательные ячейки памяти. Операнд 0 - первая из
последовательных ячейки памяти, операнд 1 - первый регистр, а
операнд 2 - константа: число последовательных регистров.
- 117 -
" addM3 "
Добавляет операнд 2 и 1 операнд, сохраняя результат в операнде 0. Все
операнды должны иметь тип M. Это может использоваться даже на двухадресных
машинах при помощи ограничений, требующих, чтобы операнды 1 и 0
находились в одном и том же месте.
" subM3 ", " mulM3 "
" divM3 ", " udivM3 ", " modM3 ", " umodM3 "
" sminM3 ", " smaxM3 ", " uminM3 ", " umaxM3 "
" andM3 ", " iorM3 ", " xorM3 "
Аналогично, но для других арифметических операций.
" mulhisi3 "
Перемножает операнды 1 и 2, которые имеют тип " HImode ", и сохраняет
" SImode " результат в операнде 0.
" mulqihi3 ", " mulsidi3 "
Аналогичные команды умножения с расширением для другой ширины.
" umulqihi3 ", " umulhisi3 ", " umulsidi3 "
Аналогичные команды умножения с расширением, но для беззнакового
умножения .
" mulM3_highpart "
Выполняет знаковое умножение операндов 1 и 2, которые имеют
Режим M, и сохраняют старшую половину результата в
операнд 0. Младшая половина результата теряется.
" umulM3_highpart "
Аналогичная команда для беззнакового умножения.
" divmodM4 "
Знаковое деление, дающее и частное, и остаток.
Операнд 1 делится на операнд 2, частное сохраняется в
операнде 0, а остаток сохраняется в операнде 3.
Для машин с командой, которая выдает и частное, и
остаток, сделайте образец для " divmodM4 " но не делайте
- 118 -
образцов для " divM3 " и " modM3 ". Это позволяет оптимизацию в
относительно общем случае, когда и частное, и остаток вычисляются.
Если команда, которая вычисляет только частное или только остаток,
существует и более эффективна, чем команда, которая вычисляет и то, и
другое, пишите программу вывода " divmodM4 " для вызова
" find_reg_note " и поиска примечания " REG_UNUSED " для частного
или остатка и генерации соответствующей команды.
" udivmodM4 "
Аналогично, но делает деление без знака.
" ashlM3 "
Арифметический сдвиг операнда 1 влево на число битов, задаваемое
операндом 2, с сохранением результата в операнде 0. Здесь M - тип
операнда 0 и операнда 1; тип операнда 2 определяется
образцом команды, и транслятор преобразует операнд к
этому типу перед генерацией команды.
" ashrM3 ", " lshrM3 ", " rotlM3 ", " rotrM3 "
Другие команды сдвига и вращения , аналогичные " ashlM3 ".
" negM2 "
Получает число, противоположного операнду 1, и записывает результата
в операнд 0.
" absM2 "
Записывает абсолютное значение операнда 1 в операнд 0.
" sqrtM2 "
Записывает квадратный корень из операнда 1 в операнд 0.
Встроенная функция C " sqrt " всегда использует тип, который
соответствует типу данных C " double ".
" ffsM2 "
Записывает в операнд 0 единицу плюс номер самого младшего из
установленных в 1 битов 1 операнда. Если операнд 1 - ноль, записывается
- 119 -
ноль. M - тип операнда 0; операнда 1 тип определяется образцом
команды, и транслятор преобразует операнд к этому типу перед генерацией
команды.
Встроенная функция C " ffs " всегда использует тип, который
соответствует типу данных C " int ".
" one_cmplM2 "
Записывает поразрядное дополнение операнда 1 в операнд 0.
" cmpM "
Сравнивает операнд 0 и операнд 1 и устанавливает условные коды.
RTL образец должен выглядеть следующим образом:
(set (cc0) (compare (match_operand:M 0 ...)
(Match_operand:M 1 ...)))
" tstM "
Сравнивает операнд 0 c нулем и устанавливает условные коды.
RTL образец должен выглядеть следующим образом:
(set (cc0) (match_operand:M 0 ...))
Образцы " tstM " не должны определяться для машин, которые не используют
" (cc0) ", поскольку это помешало бы оптимизации, так как перестало бы
быть понятным, какие операции " set " являются сравнениями.
Вместо них должны использоваться образцы " cmpM ".
" movstrM "
Команда перемещения блока. Адреса исходной строки и строки-результата
- первые два операнда, и они оба имеют тип
" Pmode ". Число перемещаемых байтов - третий операнд, в типе M.
Четвертый операнд - известное общее выравнивание исходной строки и
строки-результата, в форме " const_int " rtx. Таким образом, если
транслятор знает, что обе строки выравниваются на границу слова,
он может задавать значение 4 для этого операнда.
- 120 -
Этим образцам не требуется специальное рассмотрение возможности того,
что источник и результат могут перекрываться.
" cmpstrM "
Команда сравнения блоков, с пятью операндами. Операнд 0 -
результат; он имеет тип M. Остальные четыре операнда соответствуют
операндам " movstrM ". Два указанных блока памяти
сравниваются побайтно в лексикографическом порядке. Результатом
команда является запись значения в операнд 0, знак которого указывает
результат сравнения.
Вычисление длины строки, с тремя операндами. Операнд 0 -
результат (типа M), операнд 1 - ссылка " mem " на
первый символ строки, операнд 2 - символ для
поиска (обычно ноль) и операнд 3 - константа, описывающая
известное выравнивание начала строки.
" floatMN2 "
Преобразует целочисленный знаковый операнд 1 (допустимый для
типа с фиксированной точкой M) к типу с плавающей точкой N и
записывает результат в операнд 0 (который имеет тип N).
" floatunsMN2 "
Преобразует целочисленный беззнаковый операнд 1 (допустимый для
типа с фиксированной точкой M) к типу с плавающей точкой N и
записывает результат в операнд 0 (который имеет тип N).
" fixMN2 "
Преобразует операнд 1 (допустимый для
типа с плавающей точкой M) к типу с фиксированной точкой N как
число со знаком и записывает результат в операнд 0 (который имеет тип N).
Результат этой команды определен только тогда, когда значение
операнда 1 - целое число.
" fixunsMN2 "
Преобразует операнд 1 (допустимый для
типа с плавающей точкой M) к типу с фиксированной точкой N как
число без знака и записывает результат в операнд 0 (который имеет тип N).
- 121 -
Результат этой команды определен только тогда, когда значение
операнда 1 - целое число.
" ffruncM2 "
Преобразует операнд 1 (допустимый для
типа с плавающей точкой M) к целому числу, по-прежнему представляемому
в типе с плавающей точкой M, и записывает результат в операнд 0
(который имеет тип M).
" fix_truncMN2 "
Подобно " fixMN2 ", но преобразует значение с плавающей точкой
типа M к целому числу.
" fix_truncMN2 "
Подобно " fixunsMN2 ", но преобразует значение с плавающей точкой
типа M к целому числу.
" truncMN "
Усекает операнд 1 (допустимый для типа M) к типу N и записывает в
операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной
точкой или оба с плавающей точкой.
" extendMN "
Расширяет знаком операнд 1 (допустимый для типа M) к типу N и записывает в
операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной
точкой или оба с плавающей точкой.
" zero_extendMN "
Расширяет нулем операнд 1 (допустимый для типа M) к типу N и записывает в
операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной
точкой.
" Экстелевидение "
Извлечекает поле битов из операнда 1 (регистра или ячейки памяти),
где операнд 2 определяет ширину в битах, а операнд 3 -
начальный бит, и записывает его в операнд 0. Операнд 0 должен иметь тип
" word_mode ". Операнд 1 может иметь тип " byte_mode " или " word_mode ";
- 122 -
часто " word_mode " позволяется только для регистров. Операнды 2 и 3
должны быть допустимы для " word_mode ".
Проход генерации RTL генерирует эту команду только с
константами в качестве операндов 2 и 3.
Значение битового поля расширяется знаком до полного целочисленного
слова перед записью в операнд 0.
" extzv "
Подобно " extv ", за исключением того, что значение битового поля
расширяется нулем.
" insv "
Записывает операнд 3 (который должен быть допустимым для " word_mode ")
в битовое поле в операнде 0, где операнд 1 определяет ширину в битах, а
операнд 2 - начальный бит. Операнд 0 может иметь тип " byte_mode " или
" word_mode "; часто " word_mode " позволяется только для регистров.
Операнды, 1 и 2 должны быть допустимыми для " word_mode ".
Проход генерации RTL генерирует эту команду только с
константами в качестве операндов 1 и 2.
" movMODEcc "
Перемещает операнд 2 или операнд 3 в операнд 0 согласно
сравнению в операнде 1. Если сравнение истинно,
операнд 2 перемещается в операнд 0, иначе перемещается операнд 3.
Режим сравниваемых операндов не обязан быть таким же, как у
перемещаемых операнды. Некоторые машины, например sparc64,
имеют команды, которые перемещают целочисленное
значение в зависимости от сравнения чисел с плавающей точкой и наоборот.
Если машина не имеет условных команд перемещения, не
определяйте эти образцы.
" sCOND "
Записывает ноль или отличное от нуля число в операнд согласно коду
- 123 -
условия. Записываемое значение отлично от нуля, если условие COND
истинно. COND - имя кода выражения операции сравнения, такого
как " eq ", " lt " или " leu ".
Вы указываете тип, который операнд должен иметь, когда Вы пишете
выражение " match_operand ". Транслятор автоматически видит, какой
тип Вы использовали и поддерживает операнд этого типа.
Значение, сохраненное для истинного условия, должно иметь 1 в младшем
бите или должно быть отрицательно. Иначе команда не подходит и Вы должны
исключить ее из машинного описания. Вы
описываете транслятору точно, какое значение записывается, определяя
макрокоманда " STORE_FLAG_VALUE " (* См.: Разное::.). Если описание
не найдено, что может использоваться для всех образцов " sCOND ", Вы
должны исключить эти операции из машинного описания.
Эти операции могут не работать, но так должно быть только в относительно
нестандартных случаях; если они не будут работать для обычных случаев,
включая целочисленные сравнения, лучше всего исключить эти образцы.
Если эти операции исключены, транслятор будет обычно
генерировать код, который копирует константу 1 в адресат и делает
условный переход вокруг присваивания нуля адресату. Если этот код
более эффективен, чем потенциальные команды, используемые для
образца " sCOND ", сопровождаемые теми, которые требуются, чтобы
преобразовать результат в 1 или ноль в " SImode ", Вы должны исключить
" sCOND " операции из машинного описания.
" bCOND "
Команда условного перехода. Операнд 0 - " label_ref ", ссылающийся
на метку, на которую делается переход. Переход происходит, если коды условия
удовлетворяют условию COND.
Некоторые машины не следуют модели, принятой здесь, в которой
команда сравнения сопровождается командой условного перехода.
В этом случае образцы " cmpM " (и " tstM ") должны
просто сохранять куда-нибудь операнды и генерировать все необходимые insns
в " define_expand " (* См.: Расширение определений::.) для
- 124 -
операций условного перехода(выполняет перехода). Всем обращениям к расширению
образцов " bCOND " должны непосредственно предшествовать обращения к
расширению образцов " cmpM " или " tstM ".
Машины, которые используют псевдорегистр для значения кода условия,
или где тип, используемый для сравнения, зависит от проверяемого
условия, должен также использовать вышеупомянутый механизм. * См.:
Образцы Переходов::
Вышесказанное применимо также к образцам " movMODEcc " и " sCOND ".
" call "
Команда вызова подпрограммы, не возвращающая значения. Операнд 0 -
вызываемая функция; операнд 1 - число байтов параметров, помещенных в
стек (в типе " SImode ", если это не " const_int ");
Операнд 2 - число регистров, используемых как операнды.
На большинстве машин операнд 2 фактически не сохранен в образце RTL.
Это делается ради некоторых RISC машин, которые
должны поместить эту информацию в код ассемблера; они могут помещать
ее в RTL вместо операнда 1.
Операнд 0 должен быть " mem " RTX, чей адрес - адрес
функции. Обратите внимание, однако, что этот адрес может быть " symbol_ref "
выражением, даже если он не был бы законным адресом памяти на
целевой машине. Если это также недопустимый параметр для команды вызова,
образцом для этой операции должен быть
" define_expand " (* См.: Расширение определений::.), который помещает
адрес в регистр и использует этот регистр в команде вызова.
" call_value "
Команда вызова подпрограммы, возвращающая значение. Операнд 0 -
аппаратный регистр, в котором значение возвращается. Имеется еще три
операнда, такие же, как три операнда команды " call "
(но с номерами, увеличенными на один).
Подпрограммы, которые возвращают объекты ` BLKmode ', используют " call "
insn.
- 125 -
" call_pop ", " call_value_pop "
Аналогичны " call " и " call_value ", за исключением того, что используются,
если определены и если " RETURN_POPS_ARGS " отлично от нуля. Они должны
сгенерировать " parallel ", который содержит и обращение к функции, и
" set ", чтобы указать корректировку, делаемую для указателя кадра.
Для машин, где " RETURN_POPS_ARGS " может быть отлично от нуля,
использование этих образцов увеличивает число функций, для которых
указатель кадра может быть удален, если это желательно.
" untyped_call "
Команда вызова подпрограммы, возвращающая значение любого типа.
Операнд 0 - вызываемая функция; операнд 1 - ячейка памяти,
где должен быть сохранен результат вызова функции; операнд
2 - выражение ` parallel ', где каждый элемент - выражение " set ",
которое указывает запись возвращаемого функцией значения
в блок результата.
Этот образец команды должен быть определен, чтобы поддерживать
" __ builtin_apply " на машинах, где для вызова подпрограммы с
произвольными параметрами или сохранения возвращенного значения
необходимы специальные команды. Этот образец команды требуется на машинах,
имеющих несколько регистров, которые могут содержать возвращаемое значение
(то есть " FUNCTION_VALUE_REGNO_P " является истиной для более, чем
одного регистра).
" return "
Команда возврата из подпрограммы. Это имя образца команды
должно быть определено только, если одиночная команда может выполнить
всю работу по возврату из функции.
Подобно образцам " movM ", этот образец также используется после фазы
генерации RTL. В этом случае он должен поддерживать машины, где для
возврата из функции обычно необходимо несколько команд, но некоторый
класс функций требует только одну команду, чтобы выполнить возврат.
Обычно соответствующие функции - это те, которые не должны сохранять
никаких регистров и не должны распределять место в стеке.
- 126 -
Для таких машин, условие, определенное в этом образце должно
быть истинным только, когда " reload_completed " отлично от нуля и
эпилог функции является одиночной командой. Для машин с
окнами регистров подпрограмма " leaf_function_p " может использоваться
для определения, требуется ли временное сохранение в окне регистра.
Машины, которые имеют условные команды возврата, должны определить
образцы, подобные следующему:
(define_insn ""
[(set (PC)
(if_then_else (match_operator
0 "comparison_operator"
[(cc0) (const_int 0)])
(return)
(pc)))]
"CONDITION"
"...")
Где CONDITION обычно - то же самое условие, что и указанное в
поименованном образце " return ".
" untyped_return "
Команда возврата из подпрограммы без контроля типов . Этот образец команды
должен быть определен для поддержки " __ builtin_return " на машинах, где
необходимы специальные команды, чтобы возвратить значение любого типа.
Операнд 0 - ячейка памяти, куда записывается результат вызова функции с
" __ builtin_apply "; операнд 1 - выражение ` parallel ', где каждый
элемент - выражение ` set ', указывающее восстановление значения,
возвращаемого функцией из блока результата.
" nop "
Пустая команда. Это имя образца команды должно быть всегда
определено, чтобы вывести пустую команду в коде ассемблера.
" (const_int 0) " будет делать это как образец RTL.
- 127 -
" indirect_jump "
Команда для перехода к адресу, который является нулевым операндом. Это
имя образца обязательно на всех машинах.
" casesi "
Команда для перехода через таблицу, включая проверку границ.
Эта команда имеет пять операндов:
1. Индекс для посылки, который имеет тип " SImode ".
2. Нижняя граница индексов в таблице, целочисленная константа.
3. Общий диапазон индексов в таблице - самый большой индекс
минус самый маленький (включая оба конца).
4. Метка, которая непосредственно предшествует таблице.
5. Метка, на которую делается переход, если индекс имеет значение вне
границ. (Если макрокоманда с машинным описанием
" CASE_DROPS_THROUGH " определена, то такой индекс влечет переход
к коду после таблицы переходов вместо
перехода к этой метке. В таком случае эта метка в действительности
не используется командой " casesi ", но она всегда
задается как операнд.)
Таблица - это " addr_vec " или " addr_diff_vec " внутри
" jump_insn ". Число элементов в таблице - один плюс
разность между верхней и нижней границами.
" tablejump "
Команда для перехода к переменному адресу. Это - низкоуровневая
возможность, которая может использоваться, чтобы выполнить таблицу
переходов, когда нет образца " casesi ".
Этому образцу требуется два операнда: адрес или смещение, и
метка, которая должна непосредственно предшествовать таблице перехода.
Если макрокоманда " CASE_VECTOR_PC_RELATIVE " определена, то первый
операнд является смещением, которое рассчитывается из адреса таблицы;
- 128 -
иначе, это - абсолютный адрес перехода. В любом случае
первый операнд имеет тип " Pmode ".
" tablejump " insn - всегда последний insn перед таблицей перехода,
которую он использует. Код ассемблера обычно не имеет потребности
в использовании второго операнда, но Вы должны соединить их в образце RTL
так, что оптимизатор перехода не будет удалять таблицу как
недостижимый код.
" save_stack_block "
" save_stack_function "
" save_stack_nonlocal "
" restore_stack_block "
" restore_stack_function "
" Restore_stack_nonlocal "
Большинство машин сохраняет и восстанавливает указатель стека, копируя
его в или из объекта типа " Pmode ". Не определяйте эти образцы на
таких машинах.
Некоторые машины требуют специальной обработки для сохранения и
восстановления указателя стека. На этих машинах определяйте образцы
соответственно нестандартным случаям, используя " define_expand " (* См.:
Расширение Определений::.), который производит требуемый insns.
Вот три типа сохранения и восстановления:
1. " save_stack_block " сохраняет указатель стека в начале
блока, который распределяет объект переменного размера, а
" restore_stack_block " восстанавливает указатель стека, когда
блок покидается.
2. " save_stack_function " и " restore_stack_function " делают
аналогичную работу для самого внешнего блока функции и
используются, когда функция распределяет объекты переменного размера
или вызывает " alloca ". Только эпилог использует восстановленный
указатель стека, позволяя более простую последовательность
сохранения и восстанавления на некоторых машинах.
3. " save_stack_nonlocal " используется в функциях, которые содержат метки
- 129 -
для перехода к вложенными функциями. Он сохраняет указатель стека
таким способом, что внутренняя функция может использовать
" restore_stack_nonlocal ", чтобы восстановить указатель стека.
Транслятор генерирует код, чтобы восстановить регистры указателей
кадра и параметров, но некоторые машины требуют сохранения и
восстановления дополнительных данных типа информации окна регистра
или обратных цепочек стека. Поместите в эти образцы insns для
сохранения и восстановления любых таких данных.
При сохранении указателя стека операнд 0 - область сохранения и
операнд 1 - указатель стека. Режим, используемый для распределения
области сохранения - тип операнда 0. Вы должны указать целый
тип или " VOIDmode ", если область сохранения не нужна в данном случае
(потому что сохранение не требуется или потому что может использоваться
сохранение в машинно-специфическую область сохранения). Операнд 0 -
указатель стека, а операнд 1 - область сохранения для операций
восстановления. Если " save_stack_block " определен, операнд 0 не должен
быть " VOIDmode ", так как такие сохранения могут произвольно вкладываться.
Область сохранения - " mem ", который является константным смещении от
" virtual_stack_vars_rtx ", когда указатель стека сохраняется для
использования нелокальных goto и " reg " в других двух случаях.
" allocate_stack "
Вычитает (или добавляет, если " STACK_GROWS_DOWNWARD " не определено) операнд 0
из указателя стека, чтобы создать пространство для динамически
распределяемых данных.
Не определяйт этот образец, если все, что должно выполняться -
вычитание. Некоторые машины требуют других операций типа исследования
стека или поддержания обратной цепочки. Определите этот образец, чтобы
сгенерировать эти операции в дополнение к модифицированию указателя стека.
5.9. Когда Порядок Образцов Играет Роль
Иногда insn может соответствовать более чем одному образцу команды. Тогда
используется образец, который появляется первым в машинном описании.
- 130 -
Следовательно, более специфические образцы (образцы, которые будут соответствовать
меньшее количество insns) и более быстрые команды (которые производят лучший
код, когда имеет место соответствие им) должны обычно идти сначала в описании.
В некоторых случаях эффект упорядочивания образцов может использоваться,
чтобы скрыть образец, когда он не является допустимым. Например, 68000 имеет
команду для преобразования слова к плавающей точке и другую для
преобразования байта к плавающей точке. Команда, преобразующая
целое число к плавающей точке может соответствовать любой из них. Мы помещаем
образец преобразования слова сначала, чтобы удостовериться, что он будет
использоваться скорее, чем другой. (Иначе большое целое число могло бы быть
сгенерировано как несколько одиночных байтов, что не работало бы.) Вместо
использования такого упорядочивания образцов можно было бы сделать образец для
convert-a-byte достаточно "умный", чтобы правильно обрабатывать любую
целочисленную константу.
5.10. Взаимозависимость Образцов
Каждое машинное описание должно иметь поименованный образец для каждого из
имен условных переходов " bCOND ". Шаблон распознавания должен всегда
иметь форму
(set (PC)
(if_then_else (COND (cc0) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))
Кроме того, каждое машинное описание должно иметь безымянный образец
для каждой из возможных обратных ветвлений. Их шаблоны
выглядят примерно так:
(set (PC)
(if_then_else (COND (cc0) (const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))
Они необходимы, потому что оптимизация перехода может превращать прямое
- 131 -
ветвление в обратное.
Часто удобно использовать конструкцию " match_operator ", чтобы
уменьшить число образцов, которые должны быть определены для ветвей.
Например,
(define_insn ""
[(set (PC)
(if_then_else (match_operator 0 "comparison_operator"
[(cc0) (const_int 0)])
(pc)
(Label_ref (match_operand 1 "" ""))))]
"CONDITION"
"...")
В некоторых случаях машины поддерживают команды, идентичные во всем, кроме
машинного типа одного или более операндов. Например, могут иметься
команды "sign-extend halfword" и "sign-extend byte" с такими образцами:
(set (match_operand:SI 0 ...)
(extend:SI (match_operand:HI 1 ...)))
(set (match_operand:SI 0 ...)
(extend:SI (match_operand:QI 1 ...)))
Целые константы не определяют машинный тип, так что команда расширения
константы может соответствовать любому образцу. Образцом, которому она будет
фактически поставлена в соответствие, будет тот, который появляется первым в
файле. Для правильных результатов это должен быть образец для самого широкого
из возможных типов (здесь - " HImode "). Если бы образец соответствовал
команде " QImode ", результаты могли бы быть неправильными, если константа
фактически не подходит для этого типа.
Такие команды расширения констант редко генерируются, потому что
они оптимизируются, но они могут иногда встречаться при неоптимизированной
трансляции.
Если ограничение в образце позволяет константу, проход перезагрузки может
- 132 -
в некоторых случаях заменять регистр на константу, разрешенную ограничением.
Аналогично для ссылок памяти. Из-за этой замены
Вы не должны обеспечивать отдельные образцы для команд инкремента и декремента.
Вместо этого они будут генерироваться из одного и того же образца, который
поддерживает insns сложения из регистра в регистр, при помощи рассмотрения
операндов и генерации соответствующей машинной команды.
5.11. Определение Образцов Команд Перехода
Для большинства машин GNU CC принимает, что машина имеет условный код.
Сравнение insn устанавливает условный код, записывая результат как знакового,
так и беззнакового сравнения в заданные операнды.
Отдельный insn ветвления проверяет код условия и производит или не производит
переход согласно его значению. Среди insns ветвления различают знаковые
и беззнаковые. Много часто встречающихся машин, типа Vax, 68000 и
32000, работают этим путем.
Некоторые машины имеют различные команды для знакового и беззнакового
сравнения и только один набор команд условного перехода. Самый простой способ
обработки этих машин состоит в том, чтобым обрабатывать их точно так же, как и
другие, до заключительной стадии, где записывается ассемблерный код. В это
время, когда выводится код для команды сравнения, нужно заглянуть вперед в
следующий переход, использующий " next_cc0_user (insn) ". (Переменная
" insn " ссылается на insn, выводящийся в данный момент, в коде записи вывода коде в
образца команды.) Если RTL говорит, что это переход без знака,
нужно выводить беззнаковое сравнение; иначе нужно выводить знаковое сравнение.
Когда ветвь сама является выводом, Вы можете обрабатывать знаковые и
беззнаковые переходы одинаково.
Причина, по которой так можно делать, заключается в том, что GNU CC всегда
генерирует пару последовательных RTL insns, возможно отделяемый " note " insns,
один, чтобы установить код условия, и один, чтобы проверить его, и хранит эту
пару до конца без изменений.
Чтобы так поступить, Вы должны определить макрокоманду машинного описания
" NOTICE_UPDATE_CC ", чтобы делать " CC_STATUS_INIT "; другими словами, нет
излишних команд сравнения.
- 133 -
Некоторые машины имеют команды одновременного сравнения и перехода и не
имеют условного кода. Подобная методика работает для них. Когда наступает
время, чтобы "вывести" команду сравнения, запишите ее операнды в двух
статических переменных. Когда выводится следующая команда одновременного
сравнения и перехода, фактически выводится команды одновременного сравнения
и перехода, которая использует записанные операнды.
Этот способ также работает при определении образцов для команды
одновременного сравнения и перехода. При оптимизирующей трансляции пара
команд сравнения и перехода будет объединена согласно этим образцам. Но этого
не происходит, если оптимизация не запрошена. Так что Вы должны использовать
одно из решений, указанных выше, в дополнение к любым специальным образцам,
которые Вы определяете.
На многих RISC машинах большинство команд не воздействует на код условия
и там может даже не быть отдельного регистра кода условия. На
этих машинах ограничение, что определение и использование кода условия должно
производиться смежными insns, не необходимо и может помешать важным
оптимизациям. Например, на IBM RS/6000, имеется задержка для принимаемых
ветвей, если регистр кода условия не установлен на три команды раньше
условного перехода. Планировщик команд не может выполнять эту оптимизацию,
если не разрешается разделите определение и использование регистра кода
условия.
На этих машинах не используйте " (cc0) ", а используйте взамен регистр для
представления кода условия. Если в машине имеется определенный регистр кода
условия, используйте аппаратный регистр. Если код условия или
результат сравнения может быть помещен в любой общий регистр, или если имеется
несколько регистров условия, используйте псевдорегистр.
На некоторых машинах тип сгенерированной команды перехода может
зависеть от способа, которым код условия был произведен; например, на
68k и Sparc установка кода условия непосредственно из команд сложения или
вычитания не очищает бит переполнения, в отличие от команды тестирования,
так что для некоторых ветвлений должны использоваться различные команды
перехода. Для машин, которые используют " (cc0) ", установка и
использование кода условия должны быть смежными (разделяясь только " note "
- 134 -
insns), позволяя использование флагов в " cc_status ". (* См.: Код Условия::.)
Кроме того, сравнение и переход insns может быть найдены друг из друга,
при помощи функции " prev_cc0_setter " и " next_cc0_user ".
Однако, это не так на машинах, которые не используют " (cc0) ". На этих
машинах нельзя сделать никаких предположений о смежности insns сравнения и
перехода, и вышеупомянутые методы не могут использоваться. Вместо этого
мы используем машинный тип регистра кода условия, чтобы записать
различные форматы регистра кода условия.
Регистры, используемые, чтобы сохранить значение кода условия, должны иметь
тип, который находится в классе " MODE_CC ". Обычно это будет " CCmode ".
Если требуются дополнительные типы (как для вышеупомянутого примера сложения
на Sparc), определите макрокоманду " EXTRA_CC_MODES ", чтобы внести в список
дополнительные требуемые типы (* См.: Код Условия::.). Также определите
" EXTRA_CC_NAMES ", чтобы внести в список имена этих типов и
" SELECT_CC_MODE ", чтобы выбрать тип операнда сравнения.
Если в течение генерации RTL известно, что потребуются различные типы,
(например, если машина имеет отдельные команды для знакового и беззнакового
сравнений, подобно большинству процессоров IBM), они могут быть определены в
это время.
Если случаи, которые требуют различных типов, получаются при
комбинировании команд, макрокоманда " SELECT_CC_MODE " определяет, какой
машинный тип должен использоваться для результата сравнения. Образцы
должны быть написаны с использованием этого типа. Для поддержки случай
случая сложения на Sparc, обсуждавшегося выше, мы имеем образец
(define_insn ""
[(set (reg: CC_NOOV 0)
(compare: CC_NOOV
(plus: SI (match_operand:SI 0 "register_operand" "%r")
(Match_operand:SI 1 "arith_operand" "rI"))
(const_int 0)))]
""
"...")
- 135 -
Макрокоманда " SELECT_CC_MODE " на Sparc возвращает " CC_NOOVmode " для
сравнения, чей параметр - " plus ".
5.12. Канонизация Команд
Часто встречаются случаи, когда несколько выражений RTL можно представить
как операцию, выполняемую одиночной машинной командой. Эта ситуация чаще всего
встречается для логических команд, команд перехода и команд умножения с
накоплением. В таких случаях транслятор пытается
преобразовать эти несколько выражений RTL в одиночную каноническую форму,
чтобы уменьшить число требуемых образцов insn.
В дополнение к алгебраическим упрощениям, выполняются следующие
канонизации:
* Для операторов сравнения и коммутации, константа всегда делается
вторым операндом. Если машина поддерживает константу только как
второй операнд, то необходима поддержка только образцов с константой в
качестве второго операнда.
Для этих операторов, если один из операндов - выражение " neg ",
" not ", " mult ", " plus ", или "minus ", то он будет первым операндом.
* Для оператора " compare " константа - всегда второй операнд
на машинах, где используется " cc0 " (* См.: Образцы Переходов::.).
На других машинах встречаются редкие случаи, когда транслятор хочет
создать " compare " с константой в качестве первого операнда.
Однако, эти случаи не достаточно общие для того, чтобы делать
образец, в котором константа является первым операндом,
если реально машина не имеет такой команды.
Операнд " neg ", " not ", " mult ", " plus ", или " minus " делается
первым операндом при тех же условиях, что и выше.
* " (minus X (const_int N)) " преобразуется в " (plus X (const_int
-N)) ".
- 136 -
* Внутри вычислений адреса (то есть внутри " mem ") левый сдвиг
преобразуется в соответствующее умножение на степень двух.
Чтобы переместить поразрядное отрицание внутри поразрядных операций
логического "И" или логического "ИЛИ", используется закон Моргана. Если
это приводит к одному операнду, являющемуся выражением " not ", это будет
первый.
Машина, имеющая команду, которая выполняет поразрядное
логическое "И" одного операнда с поразрядным отрицанием другого,
должна определять образец для этой команды так:
(define_insn ""
[(set (match_operand:M 0 ...)
(and:M (not:M (match_operand:M 1 ...))
(match_operand:M 2 ...)))]
"..."
"...")
Аналогично, образец для "NAND" команды должен записываться так:
(define_insn ""
[(set (match_operand:M 0 ...)
(ior: M (not:M (match_operand:M 1 ...))
(not:M (match_operand:M 2 ...))))]
"..."
"...")
В обоих случаях необязательно включать образцы для многих
логически эквивалентных выражений RTL.
* Единственые возможные выражения RTL, включающие как поразрядное
исключающее "ИЛИ", так и поразрядное отрицание - это " (xor:M X Y) "
и " (not:M (xor:M X Y)) ".
* Сумма трех элементов, один из которых - константа, будет появляться
только в форме
- 137 -
(plus:M (plus:M X Y) CONSTANT)
* На машинах, которые не используют " cc0 ", " (compare X (const_int 0)) "
будет преобразовано в X.
* Сравнения на равенство группы битов (обычно одиночного бита)
с нулем будет записаны, используя " zero_extract " скорее, чем
эквивалент операций " and " или " sign_extract ".
5.13. Машинно - специфические локальные оптимизации
В дополнение к образцам команд " md " файл может содержать
определения машинно - специфических peephole оптимизаций.
Peephole оптимизация - это оптимизация " просмотром вперед ", т.е. поиск
команд, которые можно объединить с данной для ускорения их выполнения.
Комбинатор не обращает внимание на некоторые peephole оптимизации, когда
поток данных в программе не предполагает, что он попробует сделать их.
Например, иногда два последовательного insns, связанные по цели, могут быть
oбъединены, даже если кажется, что второй не использует регистр,
вычисленный в первом. Машинно - специфический peephole оптимизатор может
обнаружить такие возможности.
Определение выглядит примерно так:
(define_peephole
[INSN-PATTERN-1
INSN-PATTERN-2
...]
"CONDITION"
"TEMPLATE"
" OPTIONAL INSN-ATTRIBUTES ")
Последний строковый операнд может быть опущен, если Вы не используете никакой
машинно-специфической информации в этом машинном описании. Если он имеется,
то он должен подчиняться тем же правилам,что и в " define_insn ".
- 138 -
В этом скелете INSN-PATTERN-1 и так далее - образцы, которые ставятся в
соответствие последовательным insns. Оптимизация применяется к
последовательности insns, когда INSN-PATTERN-1 соответствует первому,
INSN-PATTERN-2 соответствует следующему, и так далее.
Каждый из insns, согласованный по peephole должен также соответствовать
" define_insn ". Peepholes проверяются только в последней стадии, непосредственно
перед генерацией кода, и только опционально. Следовательно, всякий insn, который
соответствовал бы peephole, но не соответствовал бы никакому " define_insn ",
вызовет аварию в генерации кода при неоптимизированной трансляции или на
различных стадиях оптимизации.
Операнды insns согласованы с " match_operands ",
" match_operator " и " match_dup ", как обычно. Необычно то, что количество
операндов относится ко всем образцам insn в определении.
Так, Вы можете проверять идентичность операндов в двух insns, используя
" match_operand " в одном insn и " match_dup " в другом.
Ограничения на операнды, используемые в образцах " match_operand " не имеют
прямого эффекта на применимости peephole, но они будет проверены
позже, так что удостоверьтесь, что ваши ограничения достаточно общие,
чтобы применять их всякий раз для peephole. Если peephole согласовано, но
ограничения не выполнены, это вызовет аварию трансляции.
Безопасно опустить ограничения во всех операндах peephole;
или Вы можете писать ограничения, которые служат, как двойная проверка на
критерии, предварительно проверенные.
Как только последовательность insns соответствует образцам, CONDITION
проверяется. Это - выражение C, которое делает окончательное решение,
выполнять ли оптимизацию (она выполняется, если выражение отлично от нуля).
Если CONDITION опущено (другими словами, строка пуста), то
оптимизация применяется к каждой последовательности insns, которая
соответствует образцам.
Определенные peephole оптимизации применяются после окончания
распределения регистров. Следовательно, peephole определение может проверять,
какие операнды заканчиваются в каких видах регистров, только просматривая
- 139 -
операнды.
Способ обратиться к операндам CONDITION состоит в том, чтобы писать
" operands[I] " для операнда номер I (как согласованному с
" (match_operand I...) "). Используйте переменную " insn ", чтобы обратиться
к последнему из согласовываемых insns; используйте " prev_active_insn ",
чтобы найти предшествующие insns.
При оптимизации вычислений с промежуточными результатами Вы можете
использовать CONDITION для создания соответствия только тогда, когда
промежуточные результаты не используются нигде в другом месте. Используйте
выражение C " dead_or_set_p (INSN, OP) ", где INSN является insn, в котором
значение, как Вы считаете, используется в последний раз
(из значения " insn ", вместе с использованием " prev_nonnote_insn "),
и OP - промежуточное значение (из " operands[I] ").
Применение средств оптимизации означает замену последовательности insns на
один новый insn. TEMPLATE управляет окончательным выводом ассемблерного кода
для этого объединенного insn. Он работает точно так же, как шаблон
" define_insn ". Количества операндов в этом шаблоне - те же, что
использовались при установлении соответствия для первоначальной
последовательности insns.
Результат определяемого peephole оптимизатора не обязан соответствовать
какому-то из образцов insn в машинном описании; он даже не
имеет возможности соответствовать им. Определение peephole оптимизатора
само служит как образец insn для управления выводом insn.
Определяемые peephole оптимизаторы выполняются во время вывода кода
ассемблера, так что insns, который они производят, никогда не объединяются
и не переставляются никаким образом.
Вот пример, взятый из машинного описания 68000:
(define_peephole
[(set (reg:SI 15) (plus:SI (reg:SI 15) (const_int 4)))
(set (match_operand:DF 0 "register_operand" "=f")
(match_operand:DF 1 "register_operand" "ad"))]
- 140 -
"FP_REG_P (operands[0]) && ! FP_REG_P (operands[1])"
"*
{
rtx xoperands[2];
xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
#ifdef MOTOROLA
output_asm_insn (\"move.l %1,(sp)\", xoperands);
output_asm_insn (\"move.l %1,-(sp)\", operands);
return \"fmove.d (sp)+,%0\";
#else
output_asm_insn (\"movel %1,sp@\", xoperands);
output_asm_insn (\"movel %1,sp@-\", operands);
return \"fmoved sp@+,%0\";
#endif
}
")
Эффект этой оптимизации заключается в изменении
jbsr _foobar
addql *4,sp
movel d1,sp@-
movel d0,sp@-
fmoved sp@+, fp0
на
jbsr _foobar
movel d1,sp@
movel d0,sp@-
fmoved sp@+,fp0
INSN-PATTERN-1 и так далее выглядят *почти* как второй операнд
" define_insn ". Имеется одно важное различие: второй операнд
" define_insn " состоит из одного или нескольких RTX, заключенных в квадратные
скобки. Обычно, имеется только один: затем то же самое действие может быть
записано как элемент " define_peephole ". Но когда в " define_insn " несколько
- 141 -
действий, они неявно включены в " parallel ". Поэтому Вы должны явно писать
" parallel " и квадратные скобки внутри него в " define_peephole ". Таким
образом, если образец insn выглядит так:
(define_insn "divmodsi4"
[(set (match_operand:SI 0 "general_operand" "=d")
(div:SI (match_operand:SI 1 "general_operand" "0")
(match_operand:SI 2 "general_operand" "dmsK")))
(set (match_operand:SI 3 "general_operand" "=d")
(mod:SI (match_dup 1) (match_dup 2)))]
"TARGET_68020"
"divsl%.l %2,%3:%0")
то способ упомянуть этот insn в peephole таков:
(define_peephole
[...
(parallel
[(set (match_operand:SI 0 "general_operand" "=d")
(div:SI (match_operand:SI 1 "general_operand" "0")
(match_operand:SI 2 "general_operand" "dmsK")))
(set (match_operand:SI 3 "general_operand" "=d")
(mod:SI (match_dup 1) (match_dup 2)))])
...]
...)
5.14. Определенные RTL последовательности для генерации кода
На некоторых целевых машинах отдельные стандартные имена образцов для RTL
поколения не могут быть обработаны одиночным insn, но их можно представить
последовательностью insns. Для этих целевых машин, Вы можете описать
'define_expand', чтобы определить, как генерировать последовательность RTL.
'define_expand' является RTL-выражением, которое работает почти подобно
'define_insn', но, в отличие от последнего, 'define_expand' используется
только для RTL поколения и он может произвести больше чем один
RTL-insn.
- 142 -
RTX 'define_expand' имеет четыре операнда:
* Имя. Каждый 'define_expand' должен иметь имя, так как обратится
к нему можно только через имя.
* RTL шаблон. Точно так же как для RTL шаблона для 'define_peephole',
это массив выражений RTL, каждый из которых - один insn.
* Условие - строка, содержащая выражение на С. Это выражение используется,
что бы выразить, как доступность этого образца зависит от подклассов
целевой машины, выбираемых с помощью командно-строчных опций, когда
GNU CC запущен. Это похоже на условие 'define_insn', которое имеет
стандартное имя. Таким образом, условие (если присутствует) может
не зависеть от данных в согласовываемом insn, но только от флагов
типа в целевой машине. Транслятор должен проверить эти условия
во время инициализации, что бы узнать какие именно названные инструкции
доступны в особенном запуске (particular run).
* Операторы подготовки - строка, содержащая ноль или больше
С-утверждений, которые должны быть выполнены до того, как из
RTL-шаблона будет сгенерирован RTL-код.
Обычно эти операторы подготавливают временные регистры для
использования в виде внутренних операндов, но они могут также
непосредственно генерировать RTL insns, вызывая подпрограммы типа
'emit_insn', и т.д. Любой такой insns предшествует insns, полученным
из RTL шаблона.
Каждому RTL insn, сгенерированному `define_expand' должен соответствовать
какой-нибудь `define_insn' в описании машины. Иначе транслятор разрушится
при попытке сгенерировать соответствующий код, или при попытке его
оптимизации.
Так же как и операторы управления RTL insns, RTL шаблон описывает операнды,
которые должны быть определены, при использовании образца.
В частности, это дает предикат для каждого операнда.
Правильный операнд, который должен быть указан при генерации RTL с
- 143 -
образца, должен быть описан с помощью 'mathch_operand' в свое первое
появления в RTL шаблоне. Это вводит информацию о предикате операнда
в таблицы, в которые записываются такие вещи. GNU CC использует эту
информацию для предзагрузки операнда в регистр, если это требуется, что бы
сделать корректный RTL код. Если на операнд ссылаются более одного раза, то
последующие ссылки должны использовать 'mathch_dup'.
RTL-шаблон может также ссылаться на внутренние 'операнды', которые
являются временными регистрами или метками, используемыми только в
последовательности, сгенерированной 'define_expand'. Внутренние операнды
в RTL шаблоне заменяются на 'match_dup', но никогда не на 'match_operand'.
Значение внутренних операндов, когда требуется использование этого образца,
не передаются транслятором как аргументы. Вместо этого они вычисляются
в образце, в операторах подготовки. Эти операторы вычисляют значения
и записывают их в соответствующие элементы 'операндов' так, чтобы
'match_dup' мог их найти.
Имеются две специальных макрокоманды, определенные для использования в
операторах подготовки: `DONE' and `FAIL'. Если поставить после них ';' то
можно использовать их как операторы.
`DONE'
Используйте макрокоманду `DONE', чтобы закончить для образца генерацию
RTL. Единственными RTL insn, полученными из образца в этом случае
будут те, которые уже сгенерированы явными обращениями к 'emit_insn' в
операторах подготовки; RTL шаблон не будет сгенерирован.
`FAIL'
Заставляет образец обломиться. Когда образец обламывается, это
означает, что образец не был по настоящему доступен.
Тогда подпрограммы вызова в трансляторе пробует другие способы для
генерации кода, используя другие образцы.
Отказ в настоящее время поддерживается только для бинарных
(сложение, умножение, смещение, и т.д.) и битовых (`extv',
`extzv', и `insv') операций .
- 144 -
Далее идет пример определения левого сдвига для чипа SPUR:
(define_expand 'ashlsi3'
[(set (match_operand:SI 0 'register_operand' '')
(ashift:SI
(match_operand:SI 1 'register_operand' '')
(match_operand:SI 2 'nonmemory_operand' '')))]
''
'
{
if (GET_CODE (operands[2]) != CONST_INT
|| (unsigned) INTVAL (operands[2]) > 3)
FAIL;
}')
Этот пример использует 'define_expand' так, что он генерирует
RTL insn для сдвига, когда величина сдвига от 0 до 3, и обламывается
в противном случае. Когда он обламывается транслятор пытается использовать
другую стратегию, используя различные образцы (такие, как library call).
Если транслятор умел бы обрабатывать нетривиальные строки условий
в образцах с именами, то было бы возможно использовать в этом случае
'define_insn'. Вот другой случай (нуль-расширение на 68000), который
более полно использует мощь 'define_expand':
(define_expand 'zero_extendhisi2'
[(set (match_operand:SI 0 'general_operand' '')
(const_int 0))
(set (strict_low_part
(subreg:HI
(match_dup 0)
0))
(match_operand:HI 1 'general_operand' ''))]
''
'operands[1] = make_safe_from (operands[1], operands[0]);')
- 145 -
Здесь сгенерированы два RTL insn - один что бы очистить весь выходной
операнд, а другой для того, что бы копировать входной операнд в его нижнею
половину. Эта последовательность некорректна если входной операнд ссылается
на выходной операнд (на его старое значение), поэтому операторы подготовки
проверяют, что это не так. Функция 'make_safe_from' копирует операнд
'operands[1]' во временный регистр если он обращается к 'operands[0]'.
Это делается с помощью генерации другого RTL insn.
Наконец, третий пример демонстрирует использование внутреннего операнда
Ноль-расширение на SPUR чипах осуществляется с помощью применения
'and' к результату и маски половины слова. Но эта маска не может быть
представлена с помощью 'const_int', потому что значение константы слишком
велико для его корректного представления на этой машине. Поэтому она должна
быть скопирована в регистр с помощью 'force_reg' и тогда этот регистр
используется в операции 'and'.
(define_expand 'zero_extendhisi2'
[(set (match_operand:SI 0 'register_operand' '')
(and:SI (subreg:SI
(match_operand:HI 1 'register_operand' '')
0)
(match_dup 2)))]
''
'operands[2]
= force_reg (SImode, gen_rtx (CONST_INT,
VOIDmode, 65535)); ')
*Замечание: Если 'define_expand' используется для того, что бы
обслужить стандартную бинарную или унарную арифметическую операцию, то
последний insn в сгенерированной последовательности не должен быть
'code_label',`barrier' или `note'. Он должен быть `insn', `jump_insn'
или `call_insn'. Если Вам не нужен настоящий insn в конце, то сгенерируйте
insn, который копирует результат операций сам в себя. Такой insn не
генерирует кода, но может предохранить от проблем в трансляторе.
5.15. Как разделять инструкции
Существует два случая, когда Вы должны определить, как происходит
- 146 -
расчленение команды на много insn. На машинах, имеющих команды,
нуждающиеся в задержке (см. Слот задержки) или команды, вывод которых
не доступен для многократных циклов. Фазы транслятора, которые оптимизируют
эти случаи должны быть способна перемещать insn в одно-командный слот
задержки. Однако, некоторые insns могут генерировать более одной машинной
команды. Эти insn не могут быть помещены в слот задержки.
Часто Вы можете заменить insn на список список insnов каждое из
которых соответствует одной машинной команде. Недостаток - медленная
трансляция, требующая большого количества памяти. Если возникающий в
результате insns слишком сложен, то оптимизация в трансляции
уменьшается. Если есть причины предположить, что разбиение insn улучшит
команду или планирование слота задержки, то транслятор его выполняет.
Фаза комбинирования insn также разбивает мнимые (putative) insn.
Если в сложном выражении, которому не находится соответствующего образца
'define_insn', три insn сливаются в один, то фаза комбинирования пытается
разбить сложный образеца в два распознающихся insn. Обычно можно
разбить сложный образец на два образца путем разбиения некоторого
подвыражения. Тем не менее, в некоторых других случаях, таких как
выполнение сложения на RISC машине большой константы в два insn,
способ разбиения операции сложения на два insn машинно-зависим.
Определение 'define_split' говорит транслятору как разбивать сложный
insn на несколько более простых. Это выглядит примерно так:
(define_split
[INSN-PATTERN]
'CONDITION'
[NEW-INSN-PATTERN-1
NEW-INSN-PATTERN-2
...]
'PREPARATION STATEMENTS')
INSN-PATTERN - образец который должен быть разбит, а CONDITION конечное
условие тестирования, как в 'define_insn'. Когда insn, ответствующий
INSN-PATTERN и удовлетворяющий COONDITION, найден, он заменяется в списке insn
на insn NEW-INSN-PATTERN-1, NEW-INSN-PATTERN-2, и т.д.
- 147 -
PREPARATION STATEMENTS подобны тем операторам, которые, определены
для 'define_expand' и выполняются прежде,
чем сгенерирован новый RTL, prepare for the generated
code or emit some insns whose pattern is not fixed. В отличие от тех
выражений `define_expand', эти выражения не должны генерировать никаких новых
псевдо регистров. Как только перезагрузка завершена, они также не должны
выделять память в странице стека.
Образцы сравниваются с INSN-PATTERN в двух различных случаях. Если insn
должен быть разбит для планирования слота задержки или для планирования insn,
то уже известно, что insn корректен. Это означает, что ему должен
соответствовать некий 'define_insn' и если `reload_completed' не ноль,
известно, что он удовлетворяет ограничениям этого 'define_insn'. В этом
случае новым insn-образцам так же должны соответствовать неким
'define_insn', и если `reload_completed' не ноль, должны так же удовлетворять
ограничениям этих определений.
Как пример этого использования `define_split', рассмотрим следующий
пример из `a29k.md', который разбивает `sign_extend' из `HImode' в
`SImode' в пару insn сдвига:
(define_split
[(set (match_operand:SI 0 'gen_reg_operand' '')
(sign_extend:SI (match_operand:HI 1 'gen_reg_operand' '')))]
''
[(set (match_dup 0)
(ashift:SI (match_dup 1)
(const_int 16)))
(set (match_dup 0)
(ashiftrt:SI (match_dup 0)
(const_int 16)))]
'
{ operands[1] = gen_lowpart (SImode, operands[1]); }')
Когда фаза комбинирования пытается разбить insn-образец, это всегда
означает, что образцу *не* соответствует никакой `define_insn'.
Проход комбинирования сперва пытается разбить одиночное выражение 'set'
- 148 -
и затем то же выражение 'set' внутри 'parallel', после которого идет
клоббирование псевдо регистра, что бы использовать его как 'scratch' регистр.
В этих случаях комбайнер точно знает, какие два новых insn-образца должны
быть сгенерированы. Он проверит, соответствуют ли эти образцы каким-нибудь
'define_insn'-определениям, поэтому Вам не нужно делать эту проверку в
'define_split' (конечно, бессмысленно писать 'define_split', который
никогда не сгенерирует соответствующих insn).
Далее идет пример такого использования `define_split', взятый из
`rs6000.md':
(define_split
[(set (match_operand:SI 0 'gen_reg_operand' '')
(plus:SI (match_operand:SI 1 'gen_reg_operand' '')
(match_operand:SI 2 'non_add_cint_operand' '')))]
''
[(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 3)))
(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 4)))]
'
{
int low = INTVAL (operands[2]) & 0xffff;
int high = (unsigned) INTVAL (operands[2]) >> 16;
if (low & 0x8000)
high++, low |= 0xffff0000;
operands[3] = gen_rtx (CONST_INT, VOIDmode, high << 16);
operands[4] = gen_rtx (CONST_INT, VOIDmode, low);
}')
Здесь предикат 'non_add_cint_operand' соответствует любому ' const_int '
который является *не* корректным операндом insn одиночного сложения.
Сложение с меньшим смещением написано так, чтобы оно могло замениться
на адрес последующей операции.
Пример из того же самого файла, который использует scratch регистр,
генерирует сравнение равенства регистра и большой константы:
- 149 -
(define_split
[(set (match_operand:CC 0 'cc_reg_operand' '')
(compare:CC (match_operand:SI 1 'gen_reg_operand' '')
(match_operand:SI 2 'non_short_cint_operand' '')))
(clobber (match_operand:SI 3 'gen_reg_operand' ''))]
'find_single_use (operands[0], insn, 0)
&& (GET_CODE (*find_single_use (operands[0], insn, 0)) == EQ
|| GET_CODE (*find_single_use (operands[0], insn, 0)) == NE)'
[(set (match_dup 3) (xor:SI (match_dup 1) (match_dup 4)))
(set (match_dup 0) (compare:CC (match_dup 3) (match_dup 5)))]
'
{
/*Берет константу С, с которой мы сравниваем и смотрим, на что она
похоже, если ее расширить до 16 бит. Потом посмотрим,
какую константу можно ХОR с С, чтобы получить расширенное знаком
значение. */
int c = INTVAL (operands[2]);
int sextc = (c << 16) >> 16;
int xorv = c ^ sextc;
operands[4] = gen_rtx (CONST_INT, VOIDmode, xorv);
operands[5] = gen_rtx (CONST_INT, VOIDmode, sextc);
}')
Чтобы избежать путаницы, не пишите один 'define_split', принимающий
как insns, соответствующие некоторому 'define_insn ', так и
несоответствующие. Вместо этого надо писать два отдельных
'define_split'-определения, одно для корректных insns, другое для некорректных.
5.16. Атрибуты команд
В дополнение к описанию команд, поддерживаемых целевой машиной, файл 'md'
также определяет группу 'атрибутов' и набор значений для каждого из них.
Каждому сгенерированному insn назначено значение для каждого атрибута.
Возможно будет один атрибут, показывающий что insn обманывает (has on)
- 150 -
машинный код условия. Этот атрибут 'NOTICE_UPDATE_CC' может затем
использоваться для отслеживания кодов условий.
5.16.1. Определение атрибутов и их значений.
Выражение `define_attr' используется для того, чтобы определить каждый
атрибут, требуемый целевой машиной. Это выглядит так:
(define_attr NAME LIST-OF-VALUES DEFAULT)
NAME - строка, определяющая имя определяемого атрибута.
LIST-OF-VALUES является либо строкой, определяющей через запятую
список значений, которые могут быть назначены атрибуту, либо пустой
строкой, которая указывает, что атрибут берет числовые значения.
DEFAULT - аттрибут-выражение, дающее значение этого атрибута для insns,
которым соответствуют образцы, чье определение не включает в себя точного
значения для этого атрибута. см. Пример Attr, для более полного описания
обработки default. см. Постоянные ограничения, для информации по атрибутам,
не зависящим ни от каких конкретных insn.
Для каждого определенного атрибута, ряд областей определения записан в
файле 'insn-attr.h'. Для тех случаев, когда для атрибута определен явный
набор значений, определено следующее:
* Описан `#define' для символа `HAVE_ATTR_NAME'.
* Определен перечисляемый класс для 'attr_NAME' с элементами формы
`UPPER-NAME_UPPER-VALUE', где имя и значение атрибута сначала
преобразуются в верхний регистр.
* Функция `get_attr_NAME' определена так, что ей передается insn и
она возвращает значение атрибута этого insn.
Например, в 'md' файле присутствует следующее:
(define_attr 'type' 'branch,fp,load,store,arith' ...)
- 151 -
следующие строки будут записаны в файл 'insn-attr.h'.
#define HAVE_ATTR_type
enum attr_type {TYPE_BRANCH, TYPE_FP, TYPE_LOAD,
TYPE_STORE, TYPE_ARITH};
extern enum attr_type get_attr_type ();
Если атрибут берет числовое значение, то 'enum' тип не будет определен,
и функция получающая значение атрибута возвратит 'int'.
5.16.1. Атрибут-выражения
Выражения RTL, используемые, чтобы определить атрибуты, используют коды,
описанные выше, плюс несколько специфических для областей определения атрибута
кодов. Эти выражения будут обсуждены далее. Атрибут-выражения
должны иметь одну из следующих форм:
`(const_int I)'
Целое число I определяет значение числового атрибута. I должно быть
неотрицателеным.
Значение числового атрибута может быть определено либо с помощью
'const_int' либо как целое число, представляемое строкой в выражениях
`const_string', `eq_attr' (см.ниже), и `set_attr'.
`(const_string VALUE)'
Строка VALUE определяет значение атрибутов константы. Если VALUE
определено как `'*'', то это означает, что по умолчанию значение
атрибута должно использоваться для insn, содержащего это выражение.
`'*'' очевидно не может использоваться в DEFAULT-выражении 'define_attr'.
Если атрибут, чье значение определяется - числовой, то VALUE должно быть
строка, содержащая неотрицательное целое число (обычно в этом случае
используется 'const_int'). Иначе, оно должно содержать одно из
корректных значений атрибута.
- 152 -
`(if_then_else TEST TRUE-VALUE FALSE-VALUE)'
TEST определяет тест атрибута, чей формат определен ниже. Значение
этого выражения - TRUE-VALUE, если ТЕST - истина и FALSE-VALUE если
ложь.
`(cond [TEST1 VALUE1 ...] DEFAULT)'
Первый операнд этого выражения - массив, содержащий четное число
выражений и состоящий из пар выражений TEST и VALUE. Значение
выражения `cond' - это то из VALUE, которое соответствует первому
истинному выражению TEST. Если все выражения TEST ложны, значение
выражения `cond' - то выражение, которое установлено DEFAULT.
Выражения TEST могут иметь одну из следующих форм:
`(const_int I)'
Этот тест истинен, если I отлично от нуля, иначе ложен.
`(not TEST)'
`(ior TEST1 TEST2)'
`(and TEST1 TEST2)'
Эти тесты истины, если соответствующая логическая функция истинна.
`(match_operand:M N PRED CONSTRAINTS)'
Этот тест истинен, если N-ый операнд insn, значение атрибута которого
определяется, имеет тип M (если M - 'VOIDmode' то эта часть теста
игнорируется ) и функция, определенная строкой PRED возвращает
не ненулевое значение, в случае если проходит операнд N и режим M
(эта часть теста игнорируется, если PRED - пустая строка).
Операнд CONSTRAINTS игнорируется и должен быть пустой строкой.
`(le ARITH1 ARITH2)'
`(leu ARITH1 ARITH2)'
`(lt ARITH1 ARITH2)'
`(ltu ARITH1 ARITH2)'
`(gt ARITH1 ARITH2)'
`(gtu ARITH1 ARITH2)'
`(ge ARITH1 ARITH2)'
- 153 -
`(geu ARITH1 ARITH2)'
`(ne ARITH1 ARITH2)'
`(eq ARITH1 ARITH2)'
Эти тесты истины, если соответствующее сравнение из двух арифметических
выражений истинно. (Арифметические выражения, сформированные с помощью
выражений `plus', `minus', `mult', `div', `mod', `abs', `neg', `and',
`ior', `xor', `not', `ashift', `lshiftrt', и `ashiftrt').
`const_int' and `symbol_ref' are always valid terms (см. Длины Insn).
`symbol_ref' - строка, означающая выражение на С, которое оставляет
'int', сосчитанный процедурой `get_attr_...'. Обычно она должна быть
глобальной переменной.
`(eq_attr NAME VALUE)'
NAME - строка, определяющая имя атрибута.
VALUE - строка, которая является либо корректным значением для атрибута
NAME, либо списком значений через запятую, либо значением, за которым
идет '!', либо списком. Если VALUE не начинается с '!', то этот тест
истинен, когда значение атрибута NAME текущего insn находится в списке,
определенном VALUE. Если ЗНАЧЕНИЕ начинается с '!', то этот тест истинен,
когда значение атрибута не в определенном списке.
Например,
(eq_attr 'type' 'load,store')
эквивалентно
(ior (eq_attr 'type' 'load') (eq_attr 'type' 'store'))
Если NAME определяет атрибут `alternative', то он относится к значению
переменной транслятора ' which_alternative '
(* note тверждение Вывода примечания::.) и значения должны быть
небольшими целыми числами.
Если NAME определяет атрибут `alternative', то это ссылка на
- 154 -
значение переменной транслятора `which_alternative' (см. Оператор вывода)
и значение должно быть маленьким целым числом. Например:
(eq_attr 'alternative' '2,3')
эквивалентно
(ior (eq (symbol_ref 'which_alternative') (const_int 2))
(eq (symbol_ref 'which_alternative') (const_int 3)))
Обратите внимание, что для большинства атрибутов тест `eq_attr'
упрощается в случаях, когда значение проверяемого атрибута известно для
всех insns, соответствующих специфическому образцу. Это намного более
общий случай.
`(attr_flag NAME)'
Значение выражения `attr_flag' истинна, если флаг, определенный с
помощью NAME - истина для 'insn', который планируется в настоящее время.
NAME - строка, указывающая один из флагов фиксированного набора, для
проверки. Проверьте флаги `forward' и `backward' чтобы определить
направление условного перехода (conditional branch). Проверите флаги
`very_likely', `likely', 'very_unlikely', and `unlikely', что бы
определить велика ли вероятность этого перехода .
Если флаг 'very_likely' истинен, то флаг 'likely' также истинен.
Аналогично для флагов `very_unlikely' и `unlikely'.
Этот пример описывает слот задержки условного перехода, который может
быть аннулирован для прямых (forward) ветвей которые исполняются
(annul-true) или для обратных (backward) ветвей, которые не исполняются
(annul-false).
(define_delay (eq_attr 'type' 'cbranch')
[(eq_attr 'in_branch_delay' 'true')
(and (eq_attr 'in_branch_delay' 'true')
(attr_flag 'forward'))
(and (eq_attr 'in_branch_delay' 'true')
- 155 -
(attr_flag 'backward'))])
Флаги `forward' и `backward' - ложь, если текущий планируемый 'insn'
не является условным переходом.
Флаги `very_likely' и `likely' истинны, если планируемый 'insn' не
является условным переходом. Флаги `very_unlikely' и `unlikely'
ложны, если планируемый 'insn' не является условным переходом.
'Attr_flag' используется только в течение планирования слота задержки
и не имеет значения для других проходов транслятора.
5.16.2. Установка значения атрибута для insns.
Значение, присвоенное атрибуту insn, предварительно определено тем,
какой из образцов согласован с этим insn (или который `define_peephole'
сгенерировал его). Каждый `define_insn' и `define_peephole' может иметь
необязательный параметр, определяющий значения атрибутов для соответствующих
insn. Атрибуту, не определенному ни в каком insn, устанавливается значение
'по умолчанию', как определено в его `define_attr'. Широкое
использование атрибутов 'по умолчанию', дает возможность определять значения
только одного-двух атрибутов большинства insn образцов, что видно в
примере из следующего раздела.
Последний аргумент `define_insn' и `define_peephole' (его можно не
использовать) - это массив выражений, каждое из которых определяет значение
единственного атрибута. Наиболее общий способ присвоения значения атрибуту -
это использование выражения `set', первый операнд которого - `attr'-выражение,
задающее имя устанавливаемого аргумента. Второй операнд `set' - выражение
задающее значение атрибута, (см. Выражения).
Когда значение атрибута зависит от атрибута `alternative' (то есть,
который является соответствующим вариантом в ограничении insn), может
использоваться выражение `set_attr_alternative '. Это позволяет
специфицировать один вектора атрибутного выражения для всех вариантов.
- 156 -
Когда не требуется общность произвольных атрибутных выражений, может
использоваться более простое выражение `set_attr', которое позволяет
определить строку, дающую либо одиночное значение атрибута либо список
значений атрибута по одному на каждый вариант.
Ниже показывается форма каждой из вышеупомянутых спецификаций. В каждом
случае NAME - строка, определяющая атрибут, который будет установлен.
`(set_attr NAME VALUE-STRING)'
VALUE-STRING либо строка, дающая требуемое значение атрибута,
либо строка, содержащая список через запятую, дающий значения для
удачных вариантов. Число элементов должно соответствовать числу
вариантов в ограничении образца insn.
Обратите внимание, что может быть полезно определить '*' для некоторого
варианта, и в таком случае атрибут примет значение по умолчанию для
insns, соответствующего этому варианту.
`(set_attr_alternative NAME [VALUE1 VALUE2 ...])'
В зависимости от варианта insn, значение будет одним из определенных
значений. This is a shorthand for using a `cond' with
tests on the `alternative' attribute.
`(set (attr NAME) VALUE)'
Первый операнд 'set' должен быть специальным выражением RTL - 'attr',
чей единственный операнд - строка, дающая имя устанавливаемому атрибуту.
VALUE - значение атрибута.
Далее показаны три различных пути представления той же самой спецификации
значения атрибута:
(set_attr 'type' 'load,store,arith')
(set_attr_alternative 'type'
[(const_string 'load') (const_string 'store')
(const_string 'arith')])
(set (attr 'type')
- 157 -
(cond [(eq_attr 'alternative' '1') (const_string 'load')
(eq_attr 'alternative' '2') (const_string 'store')]
(const_string 'arith')))
Выражение `define_asm_attributes' обеспечивает механизм определения
атрибутов, назначенных insns, который получен из asm-операторов. Оно имеет
следующую форму:
(define_asm_attributes [ATTR-SETS])
где ATTR-SETS определен также как и для обоих выражений 'define_insn' и
`define_peephole '.
Эти значения будут обычно 'худшими' значениями атрибута. Например,
они могли бы указывать, что код условия будет уничтожен.
Спецификация для атрибута `length' обрабатывается особенно. Способ
вычисления длины 'asm' insn состоит в умножении длины, определенной в
выражении 'define_asm_attributes' на число машинных команд, определенных
в 'asm' операторе, определяемое с учетом числа точек с запятой и newlins в
строке. Следовательно, значение атрибута 'length', определенного в
'define_asm_attributes' должно быть максимальной возможной длиной одиночной
машинной команды.
5.16.3. Примеры спецификаций атрибута
Разумное использование значений по умолчанию важно для эффективного
использовании insn атрибутов. Обычно, insns разделены на 'типы' и атрибут,
обычно называемый 'type', используемый, для представления это значение.
Этот атрибут обычно используется только, чтобы определить значение по
умолчанию для других атрибутов. Пример разъяснит это использование.
Предположим, что мы имеем RISC машину с кодом условия и в которой в
регистрах выполняются только операции с полным словом. Давайте предположим,
что мы можем разделить все insns на загружающие, запоминающие, выполняющие
(целые) арифметические операции, операции с плавающей точкой и условные.
- 158 -
Сейчас мы займемся определением действия insn на код условия и ограничим
себя следующими возможными эффектами: Код условия может быть установлен
непредсказуемо (clobbered), не измениться, быть установленным в зависимости
от результатов операций, или изменяться только, если элемент предварительно
установленный в коде условия был изменен.
Далее идет часть выборки файла 'md' для такой машины:
(define_attr 'type' 'load,store,arith,fp,branch' (const_string 'arith'))
(define_attr 'cc' 'clobber,unchanged,set,change0'
(cond [(eq_attr 'type' 'load')
(const_string 'change0')
(eq_attr 'type' 'store,branch')
(const_string 'unchanged')
(eq_attr 'type' 'arith')
(if_then_else (match_operand:SI 0 '' '')
(const_string 'set')
(const_string 'clobber'))]
(const_string 'clobber')))
(define_insn ''
[(set (match_operand:SI 0 'general_operand' '=r,r,m')
(match_operand:SI 1 'general_operand' 'r,m,r'))]
''
'@
move %0,%1
load %0,%1
store %0,%1'
[(set_attr 'type' 'arith,load,store')])
Обратите внимание, что мы принимаем в вышеупомянутом примере, что
арифметические операции, выполняемые в количествах меньше чем машинное слово
clobber код условия, так как они установят код условия к значению,
соответствующему полноно-словному результату.
5.16.4. Вычисление Длины Insn
- 159 -
На многих машинах предусмотрены многочисленные типы инструкций ветвления,
каждый для своей длины условного смещения. В большинстве случаев, ассемблер
выберет правильную инструкцию. Однако, когда ассемблер не может этого сделать,
GCC может, если специальный атрибут, атрибут 'length', определен. Этот атрибут
должен по определению иметь числовые значения определяемые при помощи
записи пустой строки в его `define_attr'.
В случае атрибута 'length', в выражениях теста допускаются две
дополнительные формы арифметических условий :
`(match_dup N)'
Это выражение обращается к адресу операнда N текущего insn, который
должен быть 'label_ref'.
`(pc)'
Она ссылается на адрес *текущего* insn. Может было бы лучше сделать
ее адресом следующего insn, но это было бы неудобным, потому что
длина текущего insn должна быть сосчитана.
Для нормальных insn длина будет определятся значением атрибута 'length'.
В случае insn-образцов `addr_vec' и `addr_diff_vec' длина вычисляется как
число векторов умноженное на размер каждого вектора.
Длины измеряются в байтах (наименьших адресуемых блоках).
Следующие макрокоманды могут использоваться, для совершенствования
вычисления длины:
`FIRST_INSN_ADDRESS'
Когда используется insn-атрибут 'length', эта макрокоманда определяет
значение, которое должно быть назначено адресу первого insn-а в функции.
Если не определена, используется 0.
`ADJUST_INSN_LENGTH (INSN, LENGTH)'
- 160 -
Если определена, то она меняет длину, назначенную для инструкции INSN
как функцию от контекста в котором она используется. LENGTH -
lvalue, содержащее изначально вычисленную длину insn-а. Это значение
следует обновлять, используя правильную длину insn. Если требуется
обновление, INSN не должна быть insn меняющей длину
(varying-length insn).
Эта макрокоманда обычно не требуется. Случай, когда она требуется -
ROMP. На этой машине размер `addr_vec' insn должен быть увеличен
на два, чтобы компенсировать тот факт, что может потребоваться
выравнивание.
Подпрограмма, которая возвращает `get_attr_length' (значение
атрибута 'length') может быть использована выходной подпрограммой,
что бы определить форму нужной инструкции ветвления, как показано в примере
ниже.
Как пример определения переходов различной длины, рассмотрим IBM 360.
Если мы примем соглашение, что в регистре будет установлен начальный
адрес функции, то мы можем прыгать на метки в 4-кб от старта, используя
4-байтную инструкцию. В противном случае, мы будем нуждаться в 6-байтной
последовательности для загрузки адреса из памяти и выполнения перехода.
На таких машинах образец для инструкции ветвления может быть определен
как следующий:
(define_insn 'jump'
[(set (pc)
(label_ref (match_operand 0 '' '')))]
''
'*
{
return (get_attr_length (insn) == 4
? \'b %l0\' : \'l r15,=a(%l0); br r15\');
}'
[(set (attr 'length') (if_then_else (lt (match_dup 0) (const_int 4096))
(const_int 4)
(const_int 6)))])
- 161 -
5.16.5. Постоянные атрибуты
Специальная форма `define_attr', где выражение для значения по умолчанию
является 'постоянным' выражением, указывает атрибут, являющийся константой
для данного запуска транслятора. Постоянные атрибуты могут быть использованы
для для определения того, какая разновидность используется. Например:
(define_attr 'cpu' 'm88100,m88110,m88000'
(const
(cond [(symbol_ref 'TARGET_88100') (const_string 'm88100')
(symbol_ref 'TARGET_88110') (const_string 'm88110')]
(const_string 'm88000'))))
(define_attr 'memory' 'fast,slow'
(const
(if_then_else (symbol_ref 'TARGET_FAST_MEM')
(const_string 'fast')
(const_string 'slow'))))
Подпрограмма сгенерированная для постоянных атрибутов не имеет параметров,
т.к. она не зависит ни от каких конкретных insn-ов. RTL-выражения,
использующиеся для определения значения постоянного атрибута, могут
использовать форму `symbol_ref', но не могут использовать ни форму
`match_operand' ни форму `eq_attr', включающие insn атрибуты.
5.16.6. Планирование слота задержки
Механизм insn атрибутов может быть использован для определения требований
к слотам задержки, если на целевой машине такие существуют. Инструкция должна
требовать слот задержки, если какие-то инструкции физически расположенные
за ней, выполняются так, как если бы были перед ней. Классическими примерами
являются инструкции перехода и вызова, которые часто выполняют следующую
инструкцию до того, как выполнен переход или вызов.
На некоторых машинах инструкции условного перехода могут дополнительно
'аннулировать' инструкции в слоте задержки. Это означает, что инструкция
- 162 -
не будет выполнена для определенных ветвей прихода. Поддерживаются оба типа
инструкций: те которые аннулируют, если ветвь - истина, и тех, которые
аннулируют, если ложь.
Планирование слота задержки отличается от планирования инструкций тем,
что нуждается ли инструкция в слоте задержки или нет, зависит только от
типа сгенерированной инструкции и не зависит от данных передаваемых
между инструкциями. Смотрите следующий раздел для обсуждения планирования
инструкций, зависящего от данных.
Требование insn, нуждающегося в одном или более слотах задержки
отображается через выражение `define_delay'. Это имеет следующую форму:
(define_delay TEST
[DELAY-1 ANNUL-TRUE-1 ANNUL-FALSE-1
DELAY-2 ANNUL-TRUE-2 ANNUL-FALSE-2
...])
TEST - проверка атрибутов, которая указывает прилагается ли этот
`define_delay' к конкретному insn. Если прилагается, то число требуемых
слотов задержки определяется длиной вектора, указанного как второй
аргумент. Insn, помещенный в слот задержки N, должен удовлетворять
проверке атрибутов DELAY-N. ANNUL-TRUE-N - тест атрибутов, который
определяет, какие insn-ы могут быть аннулированы, если ветвь - истина.
Аналогично, ANNUL-FALSE-N определяет какие insn-ы в слоте задержки
могут быть аннулированы, если ветвь - ложь. Если аннулирование не
поддерживается этим слотом задержки, то нужно написать `(nil)'.
Например, в часто встречаемом случае, где insn-ы ветвления и вызова
требуют один слот задержки, который может содержать любой insn, отличный
от insn-ов ветвления и вызова, в `md' файл было бы помещено следующее:
(define_delay (eq_attr 'type' 'branch,call')
[(eq_attr 'type' '!branch,call') (nil) (nil)])
Может быть определено множество выражений `define_delay'. В этом случае
каждое такое выражение определяет различные требования слота задержки
и не должно быть не одного insn-а, для которого верны хотя бы два
- 163 -
из тестов выражений `define_delay'.
Например, если мы имеем машину, требующую один слот задержки для
ветвлений и два для вызовов, то никакой слот задержки не может содержать
insn ветвления или вызова, и любой корректный insn в слоте задержки для
ветвления может быть аннулирован, если ветвь - истина, мы можем представить
это следующим образом:
(define_delay (eq_attr 'type' 'branch')
[(eq_attr 'type' '!branch,call')
(eq_attr 'type' '!branch,call')
(nil)])
(define_delay (eq_attr 'type' 'call')
[(eq_attr 'type' '!branch,call') (nil) (nil)
(eq_attr 'type' '!branch,call') (nil) (nil)])
5.16.7. Определения функциональных модулей
На большинстве RISC машин имеются команды, чьи результаты недоступны для
определенного числа циклов. В общем случае, команды загружают данные из
памяти. На многих машинах случится pipeline stall, если данные запрошены
слишком рано после команды загрузки.
Кроме того, многие более новые микропроцессоры имеют многофункциональные
модули - обычно один для целого числа и один для плавающей точки, и часто,
когда требуемый результат еще не готов, будет возникать pipeline stall.
Описания в этом разделе позволяют определить, сколько времени должно
протечь между выполнением команды и временем, когда используется результат.
Они также позволяют определить, когда выполнение команды задержит выполнение
подобных команд.
Для целей определения в этом разделе, машина разделена на 'функциональные
модули', каждый из которых выполняет специфический класс команд в порядке
'первый-вошел-первый-вышел'. Функциональные модули, которые принимают одну
- 164 -
команду за каждый цикл и позволяют результату использоваться в преуспевающей
команде (обычно через пересылку) не должны быть определены. Классические RISC
микропроцессоры будут обычно иметь одиночный функциональный модуль, который
мы можем называть `memory'. Более новые 'superscalar' процессоры будут
часто иметь функциональные модули для операций с плавающей точкой,
обычно по крайней мере для вещественного сложения и умножения.
Каждое использование функционального модуля классом insn определяется
выражением `define_function_unit', которое выглядит приблизительно так:
(define_function_unit NAME MULTIPLICITY SIMULTANEITY
TEST READY-DELAY ISSUE-DELAY
[CONFLICT-LIST])
NAME - строка, дающая имя функционального модуля.
MULTIPLICITY - целое число, определяющее число идентичных модулей в
процессоре. Если определен больше чем один модуль, то их планирование
будет происходить независимо. Должны быть учтены только по-настоящему
независимые модули. (Единственный общий пример машины, которая
имеет несколько функциональных модулей для одного класса команды,
являющиеся по-настоящему независимыми и не pipelined - два модуля
умножения и два модуля сложения в CDC 6600.)
SIMULTANEITY определяет максимальный номер insns, который может
выполняться в каждом образце функционального модуля одновременно, или ноль,
если модуль - pipelined и не имеет никаких ограничений.
Все определения `define_function_unit', касающиеся функционального
модуля NAME, должны иметь те же самые имена и значения для MULTIPLICITY и
SIMULTANEITY.
TEST - тест атрибута, который выбирает insns, описываемый в этом определение.
Обратите внимание, что insn может использовать более чем один функциональный
модуль, а функциональный модуль может быть определен в более чем одном
'define_function_unit'.
READY-DELAY - целое число, которое определяет количество циклов, после
- 165 -
которого результат команды может использоваться без всякой задержки.
? ISSUE-DELAY - целое число, которое определяет количество циклов после того,
как команда, соответствующая выражению TEST начинает использовать этот модуль,
до того как может начаться последующая команда. Цена N указывает
задержку цикла номер N-1. Последующая команда может также быть отсрочена,
если более ранняя команда имеет большее значение READY-DELAY. Этот эффект
блокирования вычислен, используя термины SIMULTANEITY, READY-DELAY,
ISSUE-DELAY, и CONFLICT-LIST. Для нормального не-pipelined функционального
модуля, в частности SIMULTANEITY работает так, что взятый модуль,
блокирует READY-DELAY циклов выполняющейся insn, и меньшее из значений
ISSUE-DELAY игнорируется.
CONFLICT-LIST - дополнительный список, дающий уточненные цены конфликта
(conflict costs) для этого модуля. Если этот список определен, то это - список
выражений теста условии, которые нужно применить к insns, выбранным для
выполнения в NAME, после insn который соответствует TEST, уже выполненному
в NAME. Для каждого insn в списке, ISSUE-DELAY определяет цену конфликта;
для insns, которого нет в списке, цена - ноль. Если этот список не определен,
CONFLICT-LIST строится по умолчанию для всех команд, использующих
функциональный модуль.
В большинстве случаев этот вектор используется, когда функциональный
модуль для поддержки плавающей точки может pipeline-уть операции
одинарной или двойной точности, но не обе сразу, или когда модуль
памяти может pipeline-ать чтение, но не запись, и т.д.
два
Как пример, рассмотрим классическую RISC машину, в которой результат
команды загрузки не доступен в течении двух циклов (требуется одиночная
команда 'delay') и в которой только одна команда загрузки может быть
выполнена одновременно. Это было бы определено как:
(define_function_unit 'memory' 1 1 (eq_attr 'type' 'load') 2 0)
Для случая плавающей точки функциональный модуль, который может pipeline
либо одиночная либо двойная точность, но не обе. Иожет быть
определено следующее:
- 166 -
(define_function_unit
'fp' 1 0 (eq_attr 'type' 'sp_fp') 4 4 [(eq_attr 'type' 'dp_fp')])
(define_function_unit
'fp' 1 0 (eq_attr 'type' 'dp_fp') 4 4 [(eq_attr 'type' 'sp_fp')])
*Замечание*: Планировщик пытается избежать конфликтов функциональных
модулей и использует все определения в выражении `define_function_unit'.
Недавно мы обратили внимание, что эти определения не могут позволять
моделирование некоторых новейших "супер скалярных" процессоров, которые
имеют insn, использующие много pipeline модулей. Эти insn могут служить
причиной потенциального конфликта для второго модуля, используемого в
течение их выполнения, и не существует способа представления этого конфликта.
Мы приветствуем любые примеры того, как происходят конфликты функциональных
модулей в таких процессорах и предположения по их представлению.
.- 167 -
6. Target Description Macros
В дополнение к файлу 'MACHINE.md', машинное описание включает файл
С-заголовка, которому традиционно дано имя 'MACHINE.h'. Этот файл заголовка
определяет многочисленные макрокоманды, которые передают информацию
относительно целевой машины, не вписывающеюся в схему файла `.md'.
Файл 'tm.h' должен быть связан с 'MACHINE.h'. Файл заголовка 'config.h'
включает 'tm.h' и большинство исходных файлов транслятора так же
включают 'config.h'.
6.1. Управление Драйвером Трансляции " gcc "
Вы можете управлять драйвером трансляции.
" SWITCH_TAKES_ARG (CHAR) "
Выражение C, которое определяет, имеет ли опция " -CHAR "
параметры. Значение должно быть числом параметров опции, и
нулем, если опций несколько.
По умолчанию эта макрокоманда определяется, чтобы правильно обрабатывать
стандартные опции. Вы не должны определять ее, если Вы не собираетесь
добавлять дополнительные опции, которые имеют параметры.
" WORD_SWITCH_TAKES_ARG (NAME) "
Выражение C, которое определяет, имеет ли опция " -NAME "
параметры. Значение должно быть числом параметров опции, и
нулем, если опций несколько. Эта макрокоманда скорее, чем
" SWITCH_TAKES_ARG ", используется для многосимвольных имен опции.
По умолчанию, эта макрокоманда определена как
" DEFAULT_WORD_SWITCH_TAKES_ARG ", которая правильно обрабатывает
стандартные опции. Вы не должны не определять " WORD_SWITCH_TAKES_ARG ",
если Вы не собираетесь добавлять дополнительные опции, которые имеют
параметры. Всякое переопределение должно вызывать
" DEFAULT_WORD_SWITCH_TAKES_ARG " и затем проверять дополнительные опции.
- 168 -
" SWITCHES_NEED_SPACES "
Строковое выражение C, которое является непустым, если компоновщику
требуется пробел между опцией " -L " или " -o " и ее параметром.
Если эта макрокоманда не определена, значение по умолчанию - 0.
" CPP_SPEC "
Строковая константа C, которая сообщает программе драйвера GNU CC опции
для передачи CPP. Она может также указывать, как транслировать опции,
которые Вы задали GNU CC, в опции GNU CC для передачи CPP.
Не определяйте эту макрокоманду, если не требуется ничего делать.
" NO_BUILTIN_SIZE_TYPE "
Если эта макрокоманда определена, препроцессор не будет определять
встроенную макрокоманду " __SIZE_TYPE__ ". Макрокоманда
" __SIZE_TYPE__ " будет определена вместо этого " CPP_SPEC ".
Она должна быть определена, если " SIZE_TYPE " зависит от машинно-зависимый
флагов, которые не доступны для препроцессора. Иначе, она не должна
быть определена.
" NO_BUILTIN_PTRDIFF_TYPE "
Если эта макрокоманда определена, препроцессор не будет определять
встроенную макрокоманду " __PTRDIFF_TYPE__ ". Макрокоманда
" __PTRDIFF_TYPE__ " будет определена вместо этого " CPP_SPEC ".
Она должна быть определена, если " PTRDIFF_TYPE " зависит от машинно-зависимый
флагов, которые не доступны для препроцессора. Иначе, она не должна
быть определена.
" SIGNED_CHAR_SPEC "
Строковая константа C, которая сообщает программе драйвера GNU CC опции
для передачи CPP. По умолчанию эта макрокоманда определена, чтобы передать опцию
" -D__CHAR_UNSIGNED__ " CPP, если " char " будет обрабатываться " cc1 "
как " unsigned char ".
- 169 -
Не определяйте эту макрокоманду, если Вам не нужно перекрывать
определение по умолчанию.
" CC1_SPEC "
Строковая константа C, которая сообщает программе драйвера GNU CC опции
для передачи " cc1 ". Она может также определять, как транслировать опции,
которые Вы задали GNU CC, в опции для GNU CC для передачи " cc1 ".
Не определяйте эту макрокоманду, если не требуется ничего делать.
" CC1PLUS_SPEC "
Строковая константа C, которая сообщает программе драйвера GNU CC опции
для передачи " cc1plus ". Она может также определять, как транслировать опции,
которые Вы задали GNU CC, в опции для GNU CC для передачи " cc1plus ".
Не определяйте эту макрокоманду, если не требуется ничего делать.
" ASM_SPEC "
Строковая константа C, которая сообщает программе драйвера GNU CC опции
для передачи ассемблеру. Она может также определять, как транслировать
которые Вы задали GNU CC, в опции для GNU CC для передачи ассемблеру.
Для примера см. файл " sun3.h ".
Не определяйте эту макрокоманду, если не требуется ничего делать.
" ASM_FINAL_SPEC "
Строковая константа C, которая сообщает программе драйвера GNU CC, как
выполнять программы, которые производят чистку после нормального ассемблера.
Обычно это не является необходимым. Для примера см. файл " mips.h ".
Не определяйте эту макрокоманду, если не требуется ничего делать.
" LINK_SPEC "
Строковая константа C, которая сообщает программе драйвера GNU CC опции
для передачи компоновщику. Она может также определять, как транслировать
опции, которые Вы задали GNU CC, в опции для GNU CC для передачи компоновщику.
Не определяйте эту макрокоманду, если не требуется ничего делать.
- 170 -
" LIB_SPEC "
Другая строковая константа C, используемая подобно " LINK_SPEC ".
Различие заключается в том, что " LIB_SPEC " используется в конце
команды, задаваемой компоновщику.
Если эта макрокоманда не определена, значение по умолчанию - то, при условии, что загрузки
Стандарт C библиотека из обычного места. См. " gcc.c ".
" LIBGCC_SPEC "
Другая строковая константа C, которая сообщает программе драйвера GNU CC,
как и когда помещать ссылку на " libgcc.a " в командную строку
компоновщика. Эта константа помещается и до, и после значения
" LIB_SPEC ".
Если эта макрокоманда не определена, драйвер GNU CC по умолчанию
передает строку " -lgcc " компоновщику, если опция " -shared " не
определена.
" STARTFILE_SPEC "
Другая строковая константа C, используемая подобно " LINK_SPEC ".
Различие заключается в том, что " STARTFILE_SPEC " используется в самом
начале команды, задаваемой компоновщику.
Если эта макрокоманда не определена, по умолчанию стандартный файл
запуска C загружается из обычного места. См. " gcc.c ".
" ENDFILE_SPEC "
Другая строковая константа C, используемая подобно " LINK_SPEC ".
Различие заключается в том, что " ENDFILE_SPEC " используется в самом
конце команды, задаваемой компоновщику.
Не определяйте эту макрокоманду, если не требуется ничего делать.
" LINK_LIBGCC_SPECIAL "
Определите эту макрокоманду, если программа драйвера сама должна искать
библиотеку " libgcc.a " и не должна передавать опции " -L " компоновщику.
Если Вы не определяете эту макрокоманду, программа драйвера передаст
- 171 -
параметр " -lgcc ", чтобы компоновщик произвел поиск и передаст ему
опции " -L ".
" LINK_LIBGCC_SPECIAL_1 "
Определите эту макрокоманду, если программа драйвера сама должна искать
библиотеку " libgcc.a ". Если Вы не определяете эту макрокоманду,
программа драйвера передаст параметр " -lgcc ", чтобы компоновщик
произвел поиск.
Эта макрокоманда подобна " LINK_LIBGCC_SPECIAL ", за исключением того,
что она не воздействуют на " -L " опции.
" MULTILIB_DEFAULTS "
Определите эту макрокоманду как выражение C для инициализатора
массива строк, чтобы сообщить программе драйвера, какие опции являются
значениями по умолчанию в данной ситуации и, таким образом, не нуждаются
в специальной обработке при использовании " MULTILIB_OPTIONS ".
Не определяйте эту макрокоманду, если " MULTILIB_OPTIONS " не определен во
фрагменте makefile адресата или если ни одна из опций, перечисленных в
" MULTILIB_OPTIONS ", не имеет значения по умолчанию. * См.: Фрагмент адресата::.
" RELATIVE_PREFIX_NOT_LINKDIR "
Определите эту макрокоманду, чтобы сообщить " gcc " что он должен
транслировать префикс " -B " в опцию " -L " компоновщика, если префикс
указывает абсолютное имя файла.
" STANDARD_EXEC_PREFIX "
Определите эту макрокоманду как строковую константу C, если Вы хотите
изменить стандартный выбор " /usr/local/lib/gcc-lib/ " как префикс по
умолчанию при поиске исполняемых файлов транслятора.
" MD_EXEC_PREFIX "
Если определено, эта макрокоманда - дополнительный префикс после
" STANDARD_EXEC_PREFIX ". " MD_EXEC_PREFIX " не ищется, если
используется опция " -b " или транслятор построен как перекрестный.
" STANDARD_STARTFILE_PREFIX "
Определите эту макрокоманду как строковую константу C, если Вы хотите
- 172 -
изменить стандартный выбор " /usr/local/lib/ " как префикс по умолчанию, чтобы
при поиске файлов запуска типа " crt0.o ".
" MD_STARTFILE_PREFIX "
Если определена, эта макрокоманда обеспечивает дополнительный префикс после
стандартных префиксов. " MD_EXEC_PREFIX " не ищется, если
используется опция " -b " или транслятор построен как перекрестный.
" MD_STARTFILE_PREFIX_1 "
Если определена, эта макрокоманда обеспечивает еще один префикс после
стандартных префиксов. Она не ищется, если
используется опция " -b " или транслятор построен как перекрестный
(кросс-транслятор).
" INIT_ENVIRONMENT "
Определите эту макрокоманду как строковую константу C, если Вы хотите
установить переменные среды для программ, вызываемых драйвером, таких,
как ассемблер и загрузчик. Драйвер передает значение этой
макрокоманды " putenv ", чтобы инициализировать необходимые переменные
среды.
" LOCAL_INCLUDE_DIR "
Определите эту макрокоманду как строковую константу C, если Вы хотите изменить
стандартный выбор " /usr/local/include " как префикс по умолчанию
при поиске локальных файлов заголовка. " LOCAL_INCLUDE_DIR "
идет перед " SYSTEM_INCLUDE_DIR " в порядке поиска.
Перекрестные трансляторы не используют эту макрокоманду и не ищут
" /usr/local/include " или его замену.
" SYSTEM_INCLUDE_DIR "
Определите эту макрокоманду как строковую константу C, если Вы хотите
указать свой каталог для поиска файлов заголовка перед
стандартным каталогом. " SYSTEM_INCLUDE_DIR " идет перед
" STANDARD_INCLUDE_DIR " в порядке поиска.
Перекрестные трансляторы не используют эту макрокоманду и не ищут
указанный каталог.
- 173 -
" STANDARD_INCLUDE_DIR "
Определите эту макрокоманду как строковую константу C, если Вы хотите изменить
стандартный выбор " /usr/include " как префикс по умолчанию
при поиске файлов заголовка.
Перекрестные трансляторы не используют эту макрокоманду и не ищут
" /usr/include " или его замену.
" INCLUDE_DEFAULTS "
Определите эту макрокоманду, если Вы хотите полностью изменить значение
по умолчанию пути поиска включаемых файлов. Заданный по умолчанию путь
поиска включает " GCC_INCLUDE_DIR ", " LOCAL_INCLUDE_DIR ",
" SYSTEM_INCLUDE_DIR ", " GPLUSPLUS_INCLUDE_DIR ", и " STANDARD_INCLUDE_DIR ".
Кроме того, " GPLUSPLUS_INCLUDE_DIR " и " GCC_INCLUDE_DIR " определяются
автоматически " Makefile " и указывают частные области поиска для
GCC. Каталог " GPLUSPLUS_INCLUDE_DIR " используется только для C++
программ.
Определение должно быть инициализаторос для массива структур.
Каждый элемент массива должен иметь два элемента: имя каталога (
строковая константа) и флаг; для C ++ - только каталоги. Массив должен
заканчиваться нулевым элементом. Вот, например, определение, используемое
для VMS:
#define INCLUDE_DEFAULTS \
{ \
{ "GNU_GXX_INCLUDE:", 1}, \
{ "GNU_CC_INCLUDE:", 0}, \
{ "SYS$SYSROOT:[SYSLIB.]", 0}, \
{ ".", 0}, \
{ 0, 0} \
}
Вот порядок префиксов для исполняемых файлов:
1. Любые префиксы, определенные пользователем с " -B ".
- 174 -
2. Переменная " GCC_EXEC_PREFIX ", если есть.
3. Каталоги, указанные в переменной среды " COMPILER_PATH ".
4. Макрокоманда " STANDARD_EXEC_PREFIX ".
5. " /usr/lib/gcc/ ".
6. Макрокоманда " MD_EXEC_PREFIX ", если есть.
Вот порядок префиксов для стартовых файлов:
1. Любые префиксы, определенные пользователем с " -B ".
2. Системная переменная " GCC_EXEC_PREFIX ", если есть.
3. Каталоги, указанные в переменной среды
" LIBRARY_PATH " (только "местная", кросс-трансляторы не используют ее).
4. Макрокоманда " STANDARD_EXEC_PREFIX ".
5. " /usr/lib/gcc/ ".
6. Макрокоманда " MD_EXEC_PREFIX ", если есть.
7. Макрокоманда " MD_STARTFILE_PREFIX ", если есть.
8. Макрокоманда " STANDARD_STARTFILE_PREFIX ".
9. " /lib/ ".
10. " /usr/lib/ ".
6.2. Целевая Спецификация времени выполнения
Вот целевые спецификации времени выполнения.
- 175 -
" CPP_PREDEFINES "
Определите ее строковой константой, содержащей " -D " опции для
определения предопределенных макрокоманд, которые идентифицируют эту
машину и эту систему. Эти макрокоманды будут предопределенными, если не
указана опция " -ansi ".
Кроме того, предопределен параллельный набор макрокоманд, чьи имена
получаются добавлением " __ " в начале и в конце. Эти
" __ " макрокоманды разрешаются ANSI стандартом, так что они
предопределены независимо от того, указано ли " -ansi ".
Например, на Sun можно использовать следующее значение:
" -Dmc68000 -Dsun -Dunix "
Результат должен определять макрокоманды " __ mc68000 __ ", " __ sun __ " и
" __ unix __ " в любом случае, и макрокоманды " mc68000 ", " sun " и
" unix ", если не указано " -ansi ".
" extern int target_flags; "
Это объявление должно присутствовать.
" TARGET_ ... "
Этот набор макрокоманд предназначен для того, чтобы позволить параметрам
команд транслятора включать или отключать использование опциональных
особенностей целевой машины. Например, одно машинное описание служит и
для 68000, и для 68020; параметр команды сообщает транслятору,
использовать ли ему команды, имеющиеся только на 68020 или нет. Этот
параметр команды работает посредством макрокоманды " TARGET_68020 "
которая проверяет бит в " target_flags ".
Определите макрокоманду " TARGET_FEATURENAME " для каждой такой опции.
Ее определение должно проверять бит в " target_flags "; например:
*define TARGET_68020 (target_flags & 1)
Одним из мест, где используются эти макрокоманды, являются
условные выражения образцов команд. Заметьте, как часто
- 176 -
" TARGET_68020 " появляется в файле машинного описания 68000, " m68k.md ".
Другое место их использования - это определение других макрокоманд в
файле" MACHINE.h ".
" TARGET_SWITCHES "
Эта макрокоманда определяет имена опций команды, чтобы устанавливать и
очищать биты в " target_flags ". Определение - инициализатор с
подгруппировкой для каждой опции команды.
Каждая подгруппировка содержит строковую константу, которая определяет
имя опции, и число, которое содержит биты для установки в
" target_flags ". Отрицательное число указывает на очистку битов;
противоположное ему число указывает, какие биты очищать. Фактическое
имя опции получается добавлением " -m " к указанному имени.
Одна из подгруппировок должна иметь пустую строку. Число в
этой группировке - значение по умолчанию для " target_flags ". Любое
действие опций начинается с этим значением.
Вот пример, который определяет " -m68000 " и " -m68020 " с
противоположными значениями и выбирает последний как значение
по умолчанию:
#define TARGET_SWITCHES \
{ { "68020", 1}, \
{ "68000", -1}, \
{ "", 1}}
" TARGET_OPTIONS "
Эта макрокоманда аналогична " TARGET_SWITCHES ", но определяет имена
опций команды, которые имеют значения. Ее определение -
инициализатор с подгруппировкой для каждой опции команды.
Каждая подгруппировка содержит строковую константу, которая определяет
фиксированную часть имени опции и адрес переменной.
Переменная типа " char * " устанавливается на переменную часть данной
опции, если фиксированная часть соответствует. Фактическое имя опции
получается добавлением " -m " к указанному имени.
- 177 -
Вот пример, который определяет " -mshort-data-NUMBER ". Если
данная опция - " -mshort-data-512 ", переменная " m88k_short_data "
будет установлена на строку " "512" ".
extern char *m88k_short_data;
#define TARGET_OPTIONS \
{ { "short-data-", &m88k_short_data } }
" TARGET_VERSION "
Эта макрокоманда - оператор C, выводящий на " stderr " строку,
описывающую конкретный выбор машинного описания. Каждое
машинное описание должно определять " TARGET_VERSION ". Например:
#ifdef MOTOROLA
#define TARGET_VERSION \
fprintf (stderr, " (68k, Motorola syntax)");
#else
#define TARGET_VERSION \
fprintf (stderr, " (68k, MIT syntax)");
#endif
" OVERRIDE_OPTIONS "
Иногда некоторые комбинации опций команды не имеют смысла на конкретной
целевой машине. Вы можете определить макрокоманду
" OVERRIDE_OPTIONS ", чтобы принять это во внимание. Эта макрокоманда,
если она определена, выполняется только однажды - сразу после того, как
все опции команды будут проанализированы.
Не используйте эту макрокоманду, чтобы включить различные дополнительные
оптимизации для " -O ". Для этого есть " OPTIMIZATION_OPTIONS ".
" OPTIMIZATION_OPTIONS (LEVEL) "
Некоторые машины могут хотеть изменить то, какие оптимизации
выполняются для различных уровней оптимизации. Эта макрокоманда, если она
определена, выполняется однажды - сразу после того, как уровень оптимизации
определен и перед тем, как остаток опций команды проанализируется. Набор
значений в этой макрокоманде используется по умолчанию
для других опций командной строки.
- 178 -
LEVEL - заданный уровень оптимизации; 2, если указано " -O2 ",
1, если указано " -O " , и 0, если ничего не указано.
Вы не должны использовать эту макрокоманду, для изменения опций, которые
не являются машинно-специфическими. Они должны одинаково зависеть от
уровня оптимизации на всех поддериваемых машинах. Используйте эту
макрокоманду для разрешения машинно-специфических оптимизаций.
* Не используйте " write_symbols " в этой макрокоманде! *
Предполагается, что отладочные опции не изменяют сгенерированный код.
" CAN_DEBUG_WITHOUT_FP "
Определите эту макрокоманду, если отладка может выполняться даже без
указателя кадра. Если эта макрокоманда определена, GNU CC будет включать
опцию " -fomit-frame-pointer " всякий раз, когда указано " -O ".
6.3. Распределение Памяти
Обратите внимание, что определения макрокоманд в этой таблице, которые являются
размерами или выравниваниями, измеряемыми в битах, не обязаны быть постоянными. Они
Это могут быть выражения C, который ссылаются на статические переменные, такие
как "target_flags ". См.: Адресат времени выполнения::.
" BITS_BIG_ENDIAN "
Задайте этому макросу значение 1, если старший бит
в байте имеет наименьший номер; иначе задайте ему значение 0.
Это означает, что побитовые команды отсчитывают биты от старшего. Если
машина не имеет побитовых команды, то значение этого макроса не играет
роли. Этот макрос не обязан быть константой.
Этот макрос не воздействует на способ упаковки полей структур,
байтами или словами; этим управляет " BYTES_BIG_ENDIAN ".
" BYTES_BIG_ENDIAN "
Задайте этому макросу значение 1, если старший байт
в слове имеет меньший номер. Этот макрос не обязан быть константой.
- 179 -
" WORDS_BIG_ENDIAN "
Задайте этому макросу значение 1 если в объекте из нескольких слов
старшее слово имеет наименьший номер. Это применяется и к ячейкам
памяти, и к регистрам; GNU CC всегда считает,что порядок слов в памяти
такой же, как и в регистрах. Этот макрос не обязан быть константой.
" LIBGCC2_WORDS_BIG_ENDIAN "
Определите эту макрокоманду, если WORDS_BIG_ENDIAN - не константа. Это должно
быть постоянное значение с тем же самым значением, что и WORDS_BIG_ENDIAN,
которое будет использоваться только при компиляции libgcc2.c. Обычно
значение устанавливается, основываясь на определениях препроцессора.
" FLOAT_WORDS_BIG_ENDIAN "
Задайте этому макросу значение 1, если числа с плавающей точкой
" DFmode ", " XFmode " или " TFmode " сохраняются в памяти со словом,
содержащим знаковый разряд по наименьшему адресу; иначе задайте ему
значение 0. Этот макрос не обязан быть константой.
Вы не должны определять этот макрос, если упорядочение такое же, как для
целых чисел из нескольких слов.
" BITS_PER_UNIT "
Задайте этому макросу значение, равное числу битов в минимальной
адресуемой ячейке памяти (байте); обычно 8.
" BITS_PER_WORD "
Число битов в слове; обычно 32.
" MAX_BITS_PER_WORD "
Максимальное число битов в слове. Если это неопределено,
значение по умолчанию - " BITS_PER_WORD ". Иначе, это - константа,
равная наибольшему значению, которое может иметь " BITS_PER_WORD " во
время выполнения.
" UNITS_PER_WORD "
Число байтов в слове; обычно 4.
" MIN_UNITS_PER_WORD "
- 180 -
Минимальное число байтов в слове. Если это неопределено,
значение по умолчанию - " UNITS_PER_WORD ". Иначе, это - константа,
равная наименьшему значению, которое может иметь " UNITS_PER_WORD " во
время выполнения.
" POINTER_SIZE "
Длина указателя в битах. Вы не должны указывать значение, большее, чем
длина " Pmode ". Если оно не равно ширине
" Pmode ", Вы должны определить " POINTERS_EXTEND_UNSIGNED ".
" POINTERS_EXTEND_UNSIGNED "
Выражение C, чье значение отлично от нуля, если указатели, которые должны
быть расширены от длины " POINTER_SIZE " до длины " Pmode ", следует
расширять знаком, и ноль, если их следует расширять нулем.
Вы не должны определять эту макрокоманду, если " POINTER_SIZE " равно
длине " Pmode ".
" PROMOTE_MODE (M, UNSIGNEDP, TYPE) "
Макрокоманда для модификации M и UNSIGNEDP, когда объект, чей тип -
TYPE и который имеет указанный тип и знаковость, должен быть
сохранен в регистре. Эта макрокоманда вызывается только тогда, когда
TYPE - скалярный тип.
На большинстве RISC машин, которые имеют операции для манипуляции только
полными регистрами, определите эту макрокоманду, чтобы установить M в
" word_mode ", если M - целочисленный тип, более узкий, чем
" BITS_PER_WORD ". В большинстве случаев расширяться должны только
целочисленные типы, потому что большая точность широких
операций с плавающей точкой обычно дороже, чем для их более узких
аналогов.
Для большинства машин макроопределение не изменяет UNSIGNEDP.
Однако некоторые машины имеют команды, которые предпочтительно
обрабатывают некоторые как знаковые, так и беззнаковые типы.
Например, на DEC Alpha 32-битные команды загрузки из памяти и 32-битные
команды сложения расширяются знаком до 64 бит. На таких машинах
устанавливайте UNSIGNEDP в зависимости от того, какой вид расширения
- 181 -
более эффективен.
Не определяйте эту макрокоманду, если она не должна изменять M.
" PROMOTE_FUNCTION_ARGS "
Определите эту макрокоманду, если продвижение, описанное " PROMOTE_MODE ",
должно быть также сделано для исходящих параметров функции.
" PROMOTE_FUNCTION_RETURN "
Определите эту макрокоманду, если продвижение, описанное " PROMOTE_MODE ",
должно быть также сделано для возвращаемых значений функции.
Если эта макрокоманда определена, " FUNCTION_VALUE " должна выполнить
те же самые продвижения, что и сделанные " PROMOTE_MODE ".
" PROMOTE_FOR_CALL_ONLY "
Определите эту макрокоманду, если продвижение, описанное " PROMOTE_MODE "
должно быть выполнено *только* для исходящих параметров функции или
возвращаемых функцией значений, как указано " PROMOTE_FUNCTION_ARGS "
и " PROMOTE_FUNCTION_RETURN ", соответственно.
" PARM_BOUNDARY "
Нормальное выравнивание, требуемое для параметров функции в стеке, в
битах. Все параметры стека получают по крайней мере это выравнивание
независимо от типа данных. На большинстве машин оно совпадает с
размером целого числа.
" STACK_BOUNDARY "
Определите эту макрокоманду, если Вы хотите сохранить некоторое
выравнивание для указателя стека. Определение - выражение C для
желаемого выравнивания (измеряемое в битах).
Если " PUSH_ROUNDING " не определено, стек будет всегда выравниваться
на указанную границу. Если " PUSH_ROUNDING " определено и
определяет менее строгое выравнивание, чем " STACK_BOUNDARY ",
выравнивание может быть потеряно на короткое время при записи параметров
в стек.
- 182 -
" FUNCTION_BOUNDARY "
Выравнивание, требуемое для точки входа в функцию, в битах.
" BIGGEST_ALIGNMENT "
Самое большое выравнивание, которое может потребоваться какому-либо
типу данных на этой машине, в битах.
" BIGGEST_FIELD_ALIGNMENT "
Самое большое выравнивание, которое может потребоваться какому-либо
полю структуры на этой машине, в битах. Если определено, оно
отменяет " BIGGEST_ALIGNMENT " для полей структур.
" MAX_OFILE_ALIGNMENT "
Самое большое выравнивание, поддерживаемое форматом объектного файла
на этой машине. Используйте эту макрокоманду, чтобы ограничить выравнивание,
которое может быть определено с использованием конструкции
" __ attribute __ ((aligned (N))) ". Если не определено, значением
по умолчанию является " BIGGEST_ALIGNMENT ".
" DATA_ALIGNMENT (TYPE, BASIC-ALIGN) "
Если определено, выражение C, чтобы вычислить выравнивание для статической
переменной. TYPE - тип данных, а BASIC-ALIGN - выравнивание, которое
объект имел бы без этой макрокоманды. Значение этой макрокоманды
используется вместо этого выравнивания для выравнивания объекта.
Если эта макрокоманда не определена, то используется BASIC-ALIGN.
Одно из применений этой макрокоманды заключается в увеличении
выравнивания данных среднего размера, чтобы все они поместились на
меньшем количестве строк кэша. Другое - выравнивать символьные массивы
на границу слова, чтобы вызовы " strcpy ", копирующие константы в
символьные массивы, можно было сделать встроенными.
" CONSTANT_ALIGNMENT (CONSTANT, BASIC-ALIGN) "
Если определено, выражение C для вычисления выравнивания для
константы, которая помещается в память. CONSTANT - константа, а
BASIC-ALIGN - выравнивание, которое объект имел бы без этой макрокоманды.
- 183 -
Значение этой макрокоманды используется вместо этого выравнивания для
выравнивания объекта.
Если эта макрокоманда не определена, то используется BASIC-ALIGN.
Типичным использованием этой макрокоманды является увеличение
выравнивание для строковой константы до выравнивания на границу слова,
так, чтобы вызовы " strcpy ", копирующие константы в символьные массивы,
можно было сделать встроенными.
" EMPTY_FIELD_BOUNDARY "
Выравнивание в битах, задаваемое битовому полю структуры, которое
следует за пустым полем, типа " int: 0; ".
Обратите внимание, что " PCC_BITFIELD_TYPE_MATTERS " также воздействует
на выравнивание, которое получается после пустого поля.
" STRUCTURE_SIZE_BOUNDARY "
Число битов, которым должен быть кратным размер любой структуры или
объединения. Размер каждой структуры и объединения округляется вверх
до его кратного.
Если Вы не определяете эту макрокоманду, значение по умолчанию
совпадает с " BITS_PER_UNIT ".
" STRICT_ALIGNMENT "
Задайте этому макросу значение 1, если команды не будут работать, в случае
если заданные данные не будут иметь номинального выравнивания. Если
команды в этом случае просто будут работать медленнее, задайте этому
макросу значение 0.
" PCC_BITFIELD_TYPE_MATTERS "
Определите это, если Вы хотите имитировать способ многих других
трансляторов C обработки выравнивания битовых полей и структур,
которые содержат их.
Поведение состоит в том, что тип, заданный для битового поля (" int ",
" short " или другой целочисленный тип) налагает выравнивание для
- 184 -
всей структуры, как будто структура действительно содержит обычное поле
этого типа. Кроме того, битовое поле помещается внутри структуры так,
чтобы это поместилось бы внутри такого поля, не пересекая его границы.
Таким образом, на большинстве машин битовое поле, чей тип описан как " int "
не будет пересекать границу четырех байт, и вынудит выравнивание на
границу четырех байт для целой структуры. (Используемое выравнивание
может быть не четыре байта; этим управляют другие параметры выравнивания.)
Если макрокоманда определена, определение должно быть выражением C;
значение выражения, отличное от нуля, допускает такое поведение.
Обратите внимание, что, если эта макрокоманда не определена, или ее
значение - ноль, некоторые битовые поля могут пересекать более чем одну
границу выравнивания. Транслятор может поддерживать такие ссылки, если
имеются insns " insv ", " extv " и " extzv ", которые могут непосредственно
ссылаться на память.
Другой известный способ работы с битовыми полями состоит в том, чтобы
определить " STRUCTURE_SIZE_BOUNDARY " такого размера, как
" BIGGEST_ALIGNMENT ". Тогда к каждой структуре можно будет обращаться
полными словами.
Если машина не имеет команд для работы с битовыми полями или если
Вы определяете " STRUCTURE_SIZE_BOUNDARY " этим путем, Вы должны задать
" PCC_BITFIELD_TYPE_MATTERS " значение, отличное от нуля.
Если ваша цель состоит в том, чтобы заставить GNU CC использовать те же
самые соглашения для расположения битовых полей, которые используются
другим транслятором, вот как исследовать то, что делает другой транслятор.
Скомпилируйте и выполните вот эту программу:
struct foo1
{
char x;
char :0;
char y;
};
- 185 -
struct foo2
{
char x;
int :0;
char y;
};
main ()
{
printf ("Size of foo1 is %d\n",
sizeof (struct foo1));
printf ("Size of foo2 is %d\n",
sizeof (struct foo2));
exit (0);
}
Если она напечатает 2 и 5, то транслятор ведет себя так же, как Вы
получили бы, определив " PCC_BITFIELD_TYPE_MATTERS ".
" BITFIELD_NBYTES_LIMITED "
Подобно PCC_BITFIELD_TYPE_MATTERS, за исключением того, что эффект
ограничивается выравниванием битовых полей внутри структуры.
" ROUND_TYPE_SIZE (STRUCT, SIZE, ALIGN) "
Определите эту макрокоманду как выражение для полного размера
структуры (задаваемой STRUCT как узел дерева), если вычисленный размер
полей - SIZE, а выравнивание - ALIGN.
Значение по умолчанию - округление SIZE вверх до числа, кратного ALIGN.
" ROUND_TYPE_ALIGN (STRUCT, COMPUTED, SPECIFIED) "
Определите эту макрокоманду как выражение для выравнивания структуры
(задаваемой STRUCT как узел дерева), если выравнивание, вычисленное
обычным путем - COMPUTED, а выравнивание, явно указанное -
SPECIFIED.
Значением по умолчанию является SPECIFIED, если оно больше; иначе
- 186 -
используется меньшее из COMPUTED и " BIGGEST_ALIGNMENT "
" MAX_FIXED_MODE_SIZE "
Целочисленное выражение для размера в битах самого большого целого числа
машинного типа, который должен фактически использоваться. Все целочисленные машинные
типы этого размера или меньше могут использоваться для структур и
объединений с соответствующими размерами. Если эта макрокоманда не
определена, предполагается " GET_MODE_BITSIZE (DImode) ".
" CHECK_FLOAT_VALUE (MODE, VALUE, OVERFLOW) "
Оператор C для проверки допустимости значения VALUE (типа " double ")
для типа MODE. Это означает, что Вы проверяете, входит ли VALUE в
диапазон возможных значений для типа MODE на этой целевой машине.
Режим MODE - всегда тип класса " MODE_FLOAT ". OVERFLOW
отлично от нуля, если значение оказывается вне диапазона.
Если VALUE недопустимо или если OVERFLOW отлично от нуля, Вы должны
установить OVERFLOW в 1 и затем присвоить какое-нибудь допустимое
значение VALUE. Прохождение недопустимого значения через транслятор может
произвести неправильный код ассемблера, который может даже повлечь
аварию ассемблеров Unix.
Эта макрокоманда не обязана определяться, если в ней нет потребности.
" TARGET_FLOAT_FORMAT "
Код, отличающий формат с плавающей точкой на целевой машине. Имеется три
определенных значения:
" IEEE_FLOAT_FORMAT "
Этот код указывает формат с плавающей точкой IEEE. Это - значение
по умолчанию; нет необходимости определять эту макрокоманду, если
формат - IEEE.
" VAX_FLOAT_FORMAT "
Этот код указывает специфический формат, используемый на Vax.
" UNKNOWN_FLOAT_FORMAT "
Этот код указывает любой другой формат.
- 187 -
Значение этой макрокоманды сравнивается с " HOST_FLOAT_FORMAT "
(* См.: Конфигурация::.), чтобы определить, имеет ли целевая машина
тот же самый формат, что и главная машина. Если на поддерживаемых машинах
фактически используются какие-то другие форматы, для них должны быть
определены новые коды.
Порядок слов, составляющих значение с плавающей запятой,
записанное в памяти, управляется " FLOAT_WORDS_BIG_ENDIAN " для
целевой машина и " HOST_FLOAT_WORDS_BIG_ENDIAN " для главной ЭВМ.
6.4. Формат Исходных Типов Данных Языка
Эти макрокоманды определяют размеры и другие характеристики
стандартных базисных типов данных, используемых в компилируемых программах.
В отличие от макрокоманд предыдущего раздела, они обращаются скорее к
специфическим особенностям C и родственных языков, чем к фундаментальным
аспектам формата хранения данных.
" INT_TYPE_SIZE "
Выражение C для размера в битах типа " int " на
целевой машине. Если Вы не определяете его, значение по умолчанию - одно
слово.
" MAX_INT_TYPE_SIZE "
Максимальное число для размера в битах типа " int " на целевой
машине. Если оно не определено, значение по умолчанию - " INT_TYPE_SIZE ".
Иначе, это - постоянное значение, которое является самым большим значением,
какое " INT_TYPE_SIZE " может иметь во время выполнения программы. Оно
используется в " cpp ".
" SHORT_TYPE_SIZE "
Выражение C для размера в битах типа " short " на целевой машине. Если
Вы не определяете его, значение по умолчанию - половина слова. (Если оно
меньше, чем одна адресуемая единица памяти, то оно округляется вверх до
ее размера.)
- 188 -
" LONG_TYPE_SIZE "
Выражение C для размера в битах типа " long " на целевой машине. Если Вы
не определяете его, значение по умолчанию - одно слово.
" MAX_LONG_TYPE_SIZE "
Максимальное число для размера в битах типа " long " на целевой
машине. Если оно не определено, значение по умолчанию - " LONG_TYPE_SIZE ".
Иначе, это - постоянное значение, которое является самым большим значением,
какое " LONG_TYPE_SIZE " может иметь во время выполнения программы. Оно
используется в " cpp ".
" LONG_LONG_TYPE_SIZE "
Выражение C для размера в битах типа " long long " на целевой машине.
Если Вы не определяете его, значение по умолчанию - два слова. Если Вы
хотите поддерживать GNU Ada на вашей машине, это значение
должно быть по крайней мере 64.
" CHAR_TYPE_SIZE "
Выражение C для размера в битах типа " char " на целевой машине. Если
Вы не определяете его, значение по умолчанию - четверть слова. (Если оно
меньше, чем одна адресуемая единица памяти, то оно округляется вверх до
ее размера.)
" MAX_CHAR_TYPE_SIZE "
Максимальное число для размера в битах типа " char " на целевой
машине. Если оно не определено, значение по умолчанию - " CHAR_TYPE_SIZE ".
Иначе, это - постоянное значение, которое является самым большим значением,
какое " CHAR_TYPE_SIZE " может иметь во время выполнения программы. Оно
используется в " cpp ".
" FLOAT_TYPE_SIZE "
Выражение C для размера в битах типа " float " на целевой машине. Если Вы
не определяете его, значение по умолчанию - одно слово.
" DOUBLE_TYPE_SIZE "
Выражение C для размера в битах типа " double " на целевой машине. Если
Вы не определяете его, значение по умолчанию - два слова.
- 189 -
" LONG_DOUBLE_TYPE_SIZE "
Выражение C для размера в битах типа " long double " на целевой машине.
Если Вы не определяете его, значение по умолчанию - два слова.
" DEFAULT_SIGNED_CHAR "
Выражение, равное 1 или 0, в зависимости от того, должен тип
" char " быть знаковым или беззнаковым по умолчанию. Пользователь всегда
может отменить это значение по умолчанию с опциями " -fsigned-char " и
" -funsigned-char ".
" DEFAULT_SHORT_ENUMS "
Выражение C, определяющее, давать ли типу " enum " только столько
байтов, сколько требуется, чтобы представить диапазон возможных значений
этого типа. Значение, отличное от нуля, означает делать это; значение ноля
означает, что все " enum " типы должны распределяться подобно " int ".
Если Вы не определяете макрокоманду, значение по умолчанию - 0.
" SIZE_TYPE "
Выражение C для строки, описывающей имя типа данных,
используемого для значений размера. Тип " size_t " определяется
с использованием содержимого строки.
Строка может содержать больше одного ключевого слова. Если это так,
отделяйте их пробелами, и пишите сначала ключевое слово, указывающее
длину, затем " unsigned ", если нужно, и в заключение " int ". Строка
должна точно соответствовать одному из имен типа данных, определенных в
функции " init_decl_processing " в файле " c-decl.c ". Вы не можете
опускать " int " или изменять порядок - это приведет к аварии при запуске
транслятора.
Если Вы не определяете эту макрокоманду, значение по умолчанию -
" "long unsigned int " ".
" PTRDIFF_TYPE "
Выражение C для строки, описывающей имя типа данных,
используемого для значений разности двух указателей. Тип " ptrdiff_t "
определяется с использованием содержимого строки. См. " SIZE_TYPE " выше
- 190 -
для более подробной информации.
Если Вы не определяете эту макрокоманду, значение по умолчанию - " " long int " ".
" WCHAR_TYPE "
Выражение C для строки, описывающей имя типа данных, используемого для
длинных символов. Тип " wchar_t " определяется с использованием
содержимого строки. См. " SIZE_TYPE " выше для более подробной информации.
Если Вы не определяете эту макрокоманду, значение по умолчанию - " "int" ".
" WCHAR_TYPE_SIZE "
Выражение C для размера в битах типа данных для длинных
символов. Это используется в " cpp ", который не может использовать
" WCHAR_TYPE ".
" MAX_WCHAR_TYPE_SIZE "
Максимальное число для размера в битах типа данных для длинных
символов. Если оно не определено, значение по умолчанию -
" WCHAR_TYPE_SIZE ". Иными словами, это - константа, которое является
самым большим значением, которое " WCHAR_TYPE_SIZE " может иметь во время
выполнения программы. Это используется в " cpp ".
" OBJC_INT_SELECTORS "
Определите эту макрокоманду, если тип селекторов Objective C должен быть
" int ".
Если эта макрокоманда не определена, то селекторы должны иметь тип
" struct objc_selector * ".
" OBJC_SELECTORS_WITHOUT_LABELS "
Определите эту макрокоманду, если транслятор может группировать все селекторы
вместе в вектор и использовать только одну метку в начале вектора. Иными
словами, транслятор должен дать каждому селектору
собственную ассемблерную метку.
На некоторых машинах важно иметь отдельную метку для
каждого селектора, потому что это дает возможность компоновщику устранить
- 191 -
двойные селекторы.
" TARGET_BELL "
Константное выражение C для целого значения управляющей
последовательности " \a ".
" TARGET_BS "
" TARGET_TAB "
" TARGET_NEWLINE "
Константное выражение C для целых значений управляющих
последовательностей " \b ", " \t " и " \n ".
" TARGET_VT "
" TARGET_FF "
" TARGET_CR "
Константное выражение C для целых значений управляющих
последовательностей " \v ", " \f " и " \r ".
6.4. Использование Регистров
В этом разделе объясняется, как описывать, какие регистры имеет целевая
машина и как (вообще) они могут использоваться.
Описание того, какие регистры могут использовать конкретные команды,
сделано с классами регистров; см. *Классы Регистров::. По поводу
информации об использовании регистров для обращения к кадру стека,
см. *Регистры Кадра::. О передаче значений в регистрах см. *Параметры Регистров::.
О возврате значений в регистрах, см. *Возврат скаляров::.
6.4.1. Базовые Характеристики Регистров
Регистры имеют различные характеристики.
" FIRST_PSEUDO_REGISTER "
Число аппаратных регистров, известных транслятору. Они получают
номера от 0 до " FIRST_PSEUDO_REGISTER-1 "; таким образом, первому
псевдорегистру действительно присваивается номер
- 192 -
" FIRST_PSEUDO_REGISTER ".
" FIXED_REGISTERS "
Инициализатор, который говорит, какие регистры используются для
фиксированных целей на протяжении всего оттранслированного кода и,
следовательно, недоступны для общего распределения. Они могут включать
указатель стека, указатель кадра (за исключением машинах, где он может
использоваться как общий регистр, если указатель кадра не нужен),
программный счетчик на машинах, где он рассматривается как
адресуемый регистр, и любой другой пронумерованный регистр со
стандартным использованием.
Эта информация выражается как последовательность чисел, разделяемых
запятыми и заключенная в фигурные скобки. N-е число равно 1, если
регистр N фиксирован, и 0 в противном случае.
Таблица, инициализированная этой макрокомандой, и таблица,
инициализированной следующей, могут быть изменены во время выполнения
программы как автоматически, под действием макрокоманды
" CONDITIONAL_REGISTER_USAGE ", так и пользователем, при помощи опций
команды " -ffixed-REG ", " -fcall-used-REG " и " -fcall-saved-REG ".
" CALL_USED_REGISTERS "
Подобно " FIXED_REGISTERS ", но имеет 1 для каждого регистра, который
затирается (вообще говоря) обращениями к функции так же, как для
фиксированных регистров. Эта макрокоманда, следовательно, идентифицирует
регистры, которые не доступны для общего распределения значений, которые
должны жить во время обращений к функции.
Если регистр имеет 0 в " CALL_USED_REGISTERS ", транслятор
автоматически сохраняет его на входе в функцию и восстанавливает его на
выходе из функции, если регистр используется внутри функции.
" CONDITIONAL_REGISTER_USAGE "
Ноль или более операторов C, которые могут условно изменять две
переменных " fixed_regs " и " call_used_regs " (обе - типа " char[] ")
после того, как они были инициализированы двумя предыдущими
макрокомандами.
- 193 -
Это необходимо в случае, если фиксированные или затиаремые при
вызове регистры зависят от целевых флажков.
Не определяйте эту макрокоманду, если не требуется ничего делать.
Если использование всего класса регистров зависит от целевых
флагов, Вы можете сообщить об этом GCC, используя эту макрокоманду для
установки " fixed_regs " и " call_used_regs " в 1 для каждого из регистров
в классах, которые не должны использоваться GCC. Также определите
макрокоманду " REG_CLASS_FROM_LETTER " так, чтобы она возвращала " NO_REGS ",
если она вызывается с символом для класса, который не должен использоваться.
(Однако, если этот класс не включен в " GENERAL_REGS " и все
образцы insn, чей ограничения разрешают этот класс,
управляются целевыми переключателями, то GCC будет автоматически избегать
использования этих регистров, когда целевые переключатели запрещают это.)
" NON_SAVING_SETJMP "
Если эта макрокоманда определена и имеет значение, отличное от нуля, это
означает, что " setjmp " и связанные с ней функции не могут сохранить
регистров, или что " longjmp " не может восстановить их. В качестве
компенсации транслятор избегает помещать переменные в регистры в
функциях, использующих " setjmp ".
" INCOMING_REGNO (OUT) "
Определите эту макрокоманду, если целевая машина имеет окна регистров.
Это выражение C возвращает номер регистра, который видит
вызываемая функция, в зависимости от номера регистра OUT, который видит
вызывающая функция. Возвратите OUT, если номер регистра OUT не
выходящий (outbound) регистр.
" OUTGOING_REGNO (В) "
Определите эту макрокоманду, если целевая машина имеет окна регистров.
Это выражение C возвращает номер регистра, который видит
вызывающая функция, в зависимости от номера регистра IN, который видит
вызываемая функция. Возвратите IN, если номер регистра IN не
входящий (inbound) регистр.
- 194 -
6.4.2. Порядок Распределения Регистров
Регистры распределяются по порядку.
" REG_ALLOC_ORDER "
Если определено, инициализатор для вектора целых чисел, содержащих
номера аппаратных регистров в порядке, в котором GNU CC предпочтительно
должен использовать их (от наиболее прдпочтительных к наименее).
Если эта макрокоманда не определена, самыми первыми используются регистры
с самыми маленькими номерами ( все, кроме эквивалентных).
Одно из применений этой макрокоманды - на машинах, где регистры с самыми
большими номерами всегда должны сохраняться и команды сохранения
нескольких регистров поддерживают только последовательности
последовательных регистров. На таких машинах определите
" REG_ALLOC_ORDER " как инициализатор, заносящий в список первыми
годные для распределения регистры с самыми большими номерами.
" ORDER_REGS_FOR_LOCAL_ALLOC "
Оператор C (без точки с запятой) для выбора порядка распределения
аппаратных регистров для псевдорегистров, локальных для базового блока.
Сохраните желательный порядок регистров в массиве " reg_alloc_order ".
Элемент 0 должен быть регистр, распределяемым первым; элемент 1 -
следующим, и так далее.
Тело макрокоманды не должно делать никаких предположений относительно
содержимого " reg_alloc_order " перед выполнением макрокоманды.
На большинстве машин нет необходимости определять эту макрокоманду.
6.4.2. Как Располагать Значения В Регистрах
В этом разделе обсуждаются макрокоманды, которые описывают, какие виды значений
- 195 -
(конкретно, какие машинные типы) каждый регистр может содержать, и сколько
последовательных регистров необходимо для данного типа.
" HARD_REGNO_NREGS (REGNO, MODE) "
Выражение C для числа последовательных аппаратных регистров,
начиная с регистра с номером REGNO, требуемого для хранения значения
типа MODE.
Например, на машине, где все регистры - ровно одно слово, эту
макрокоманду следует определить так:
#define HARD_REGNO_NREGS(REGNO, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \
/ UNITS_PER_WORD))
" HARD_REGNO_MODE_OK (REGNO, MODE) "
Выражение C, которое отлично от нуля, если допустимо сохранение
значения типа MODE в аппаратном регистре с номером REGNO (или в
нескольких регистрах, начиная с него). Для машины, где все
регистры одинаковы, определения должно выглядеть так:
#define HARD_REGNO_MODE_OK (REGNO, MODE) 1
Для этой макрокоманды не является необходимой проверка номеров
фиксированных регистров, потому что механизм распределения рассматривает
их как всегда занятые.
На некоторых машинах, значения двойной точности должны храниться в
четных/нечетных парах регистров. Способ сделать это заключается в том, чтобы
запретить в этой макрокоманде нечетные номера регистров для таких типов.
Минимальное требование для типа, чтобы он подходил для регистра,
заключается в том, что образец команды " movMODE " поддерживает
перемещение между регистром и любым другим аппаратным регистром, для
которого этот тип подходит; и это перемещение значения в регистр и
обратно не изменяет его.
Так как та же самая команда, используемая для передвижения " SImode ",
будет работать для всех более узких целочисленных типов, для
- 196 -
" HARD_REGNO_MODE_OK " не обязательно на любой машине различать эти
типы, если Вы определяете образцы " movhi ", и т.д., чтобы
воспользоваться преимуществом этого. Это полезно из-за взаимодействия между " HARD_REGNO_MODE_OK "
и " MODES_TIEABLE_P "; очень желательно, чтобы все целочисленные типы
были связываемы таким образом.
Многие машины имеют специальные регистры для арифметики с плавающей
точкой. Часто люди считают, что машинные типы с плавающей точкой
позволяются только в регистрах с плавающей точкой. Это не так. Любые
регистры, которые могут хранить целые числа, могут безопасно *сохранять*
машинный тип с плавающей точкой, независимо от того, может или нет
выполняться в этих регистрах плавающая арифметика. Целочисленные команды
перемещения могут использоваться и для перемещения этих значений.
На некоторых машинах, однако, верно обратное: машинные типы с
фиксированной точкой не могут находиться в плавающих регистрах. Это так,
если плавающие регистры нормализуют любое значение, сохраненное в них,
потому что сохранение не-плавающего значения там исказило бы его. В этом
случае " HARD_REGNO_MODE_OK " должен запретить машинные типы с
фиксированной точкой в плавающих регистрах. Но если плавающие регистры не
нормализуются автоматически, если Вы можете сохранять любой набор битов в
регистр и восстанавливать его без изменений, то любой машинный тип
может храниться в плавающем регистре, и Вы можете определять с учетом
этого эту макрокоманду.
Основное значение специальных плавающих регистров - это то, что к ним
применимы команды арифметики с плавающей точкой. Однако это не имеет
никакого отношения к " HARD_REGNO_MODE_OK ". Вы обрабатываете это,
записывая соответствующие ограничения для этих команд.
На некоторых машинах доступ к плавающим регистрам особенно медленный,
так что лучше хранить значение во кадре стека, чем в таком регистре,
если с ним не выполняется команд плавающей арифметики. Поскольку
плавающие регистры не содержатся в классе " GENERAL_REGS ", они не будут
использоваться, если этого не потребует какое-нибудь ограничение образца.
" MODES_TIEABLE_P (MODE1, MODE2) "
Выражение C, которое отлично от нуля, если желательно выбирать
- 197 -
распределение регистров так, чтобы избегать команд перемещения между a
значениями типа MODE1 и значениями типа MODE2.
Если " HARD_REGNO_MODE_OK (R, MODE1) " и " HARD_REGNO_MODE_OK (R,
MODE2) " являются различным для любого R, то " MODES_TIEABLE_P (MODE1,
MODE2) " должно быть нулем.
Обработка leaf-функций
На некоторых машинах, leaf-функция (то есть та, которая не производит никаких
вызовов) может выполняться более эффективно, если не делает собственного окна
регистров. Часто это означает, что она требует, чтобы параметры были в
регистрах, в которые их передает вызывающая функция, вместо регистров, через
которые они обычно поступают.
Специальная обработка для leaf-функций применяется только когда другие условия
выполнены; например, часто они могут использовать только эти же регистры для
собственных переменных и временных данных. Мы используем термин "leaf-функция",
чтобы обозначить функцию, которая подходит для этой специальной обработки, так
что функции без обращений не обязательно - "leaf-функции".
GNU CC назначает номера регистрам прежде чем узнает, является ли функция
подходящая для обработки как leaf-функция. Так что ему требуется перенумеровать
регистры, чтобы получить leaf-функцию. Следующие макрокоманды выполняют это.
"LEAF_REGISTERS' ("leaf-регистры")
Инициализатор C для вектора, индексированного номерами аппаратных регистров,
который содержит 1 для регистра, который является кандидатом на обработку для
leaf-функции.
Если обработка leaf-функции включает перенумеровку регистров, то регистры,
отмеченные выше, должны содержать 1 перед перенумерацией, как GNU CC обычно и
распределяет. Регистры, которые будут фактически использоваться в ассемблерном
коде, после изменения нумерации, не должны быть отмечены 1 в этом векторе.
Определите эту макрокоманду только если целевая пытается оптимизировать
обработку leaf-функций.
- 198 -
"LEAF_REG_REMAP (REGNO)"
Выражение C, значение которого - номер регистра, которому должен быть присвоен
номер REGNO, когда функция обрабатывается как leaf-функция.
Если REGNO - номер регистра, который не должен появиться в leaf-функции перед
изменением нумерации, то выражение должно выдать -1, что заставит транслятор
прерваться.
Определите эту макрокоманду только если целевая машина пытается оптимизировать
обработку leaf-функций, и чтобы сделать это, регистры должны быть
перенумерованы.
Обычно, "FUNCTION_PROLOGUE" и "FUNCTION_EPILOGUE" ("пролог функции" и "эпилог
функции") должны обрабатывать leaf-функции особо. Они могут проверить
переменную C "leaf_function", которая отлична от нуля для leaf-функций.
(Переменная "leaf_function" определена только если "LEAF_REGISTERS"
определена.)
6.4.3. Регистры, образующие стек
На компьютерах, где некоторые из регистров образуют стек (как в 80387
сопроцессоре для 80386), имеются некоторые особенности. Регистры стека обычно
записываются, проталкиванием в стек, и пронумерованы относительно вершины
стека.
В настоящее время, GNU CC может обрабатывать только одну группу стекоподобных
регистров, а они должны быть последовательно пронумерованы.
"STACK_REGS' ("Регистры стека")
Определите ее, если машина имеет стекоподобные регистры.
"FIRST_STACK_REG" ("Первый регистр стека")
Номер первого стекоподобного регистра. Это - вершина стека.
"LAST_STACK_REG" ("Последний регистр стека")
Номер последнего стекоподобного регистра. Этот - дно стека.
- 199 -
6.4.4. Устаревшие Макрокоманды для Управления Использованием Регистров
Эти возможности работают не очень хорошо. Они существуют, потому что требуются
для генерации правильного кода для 80387 сопроцессора 80386. Они больше не
используются в соответствии c машинным описанием и могут быть удалены в более
поздней версии транслятора. Не используйте их!
"OVERLAPPING_REGNO_P (REGNO)" ("Перекрывающиеся номера регистров")
Если определена, то это - выражение C, значение которого отлично от нуля, если
аппаратный номер регистра REGNO - перекрывающийся регистр. Это означает
аппаратный регистр, который перекрывается c аппаратным регистром с другим
номером. (Такое перекрытие нежелательно, но иногда позволяет поддерживаться
машине, которая иначе не поддерживалась бы.) Эта макрокоманда должна возвратить
значение, отличное от нуля, для *всех* регистров, которые накладываются на друг
друга. GNU CC может использовать накладывающийся регистр только в некоторых
ограниченных случаях. Они могут использоваться для распределения внутри
базисного блока и могут быть spilled для перезагрузки; вот и все.
Если эта макрокоманда не определена, это означает, что ни один из аппаратных
регистров не накладывается друг на друга. Это - обычная ситуация.
"INSN_CLOBBERS_REGNO_P (INSN, REGNO)"
Если определена, то это - выражение C, значение которого должно быть отлично от
нуля, если insn INSN имеет эффект таинственного затирания (clobbering)
содержимого аппаратного регистра номер REGNO. "Таинственного" потому, что
выражение insn RTL не описывает такого эффекта.
Если эта макрокоманда не определена, это означает, что никакая insn не clobbers
регистры. Это - обычная ситуация; при прочих равных условиях, выражению RTL
лучше всего показать весь процесс.
"PRESERVE_DEATH_INFO_REGNO_P (REGNO)"
Если определена, то это - выражение C, значение которого отлично от нуля, если
точные "REG_DEAD" замечания необходимы для номера аппаратного регистра REGNO во
время получения ассемблерного кода. Когда это так, некоторые оптимизации,
которые происходят после распределения регистров и могли бы свести на нет эти
- 200 -
замечания, не выполняются, когда этот регистр включается в процесс.
Вы могли бы принять меры, чтобы предотвратить "смертельную" информацию о
регистре, когда некоторый код в машинном описании, который выполняется, чтобы
писать ассемблерный код, видит "смертельные" замечания. Это необходимо только,
когда фактическая аппаратная возможность, о которой GNU CC думает как о
регистре, не является регистром обычного типа. (Это может быть, например,
аппаратный стек.)
Если эта макрокоманда не определена, это означает, что никакие "смертельные"
примечания не должны сохраняться. Это - обычная ситуация.
6.5. Классы Регистров
На многих машинах пронумерованные регистры - не все эквивалентны. Например,
некоторые регистры нельзя использовать для индексной адресации; некоторые
регистры нельзя использовать в некоторых командах. Эти машинные ограничения
известны транслятору через "классы регистров".
Вы определяете ряд классов регистров, придавая каждому имя и говоря, какие из
регистров принадлежат ему. Затем вы можете задать классы регистров, которые
разрешены как операнды в конкретных командах.
Вообще, каждый регистр будет принадлежать нескольким классам. Фактически, один
класс должен быть назван "ALL_REGS" ("все регистры") и содержать все регистры.
Другой класс должен быть назван "NO_REGS" ("никаких регистров") и не содержать
никаких регистров. Часто объединение двух классов будет другим классом, однако
это необязательно.
Один из классов должен быть назван "GENERAL_REGS" ("регистры общего
назначения"). Имя не столь важно, но символы "r" и "g" определяет этот класс.
Если "GENERAL_REGS' - то же, что "ALL_REGS", просто определите это как
макрокоманду, которая разворачивается до "ALL_REGS".
Упорядочьте классы так, чтобы, если класс X содержится в классе Y то X, имел
меньший номер класса чем Y.
- 201 -
Классы, отличные от "GENERAL_REGS", указываются в ограничениях на операнд
через машинно-зависимые символы ограничения операнда. Вы можете определять
такие символы, чтобы отвечать различным классам, затем использовать их в
ограничениях на операнд.
Вы должны определить класс для объединения двух классов всякий раз, когда
некоторая команда позволяет использовать оба класса. Например, если команда
позволяет использовать как регистр с плавающей точкой (сопроцессорный), так и
регистр общего назначения для некоторого операнда, вы должны определить класс
"FLOAT_OR_GENERAL_REGS" ("вещественные или общие регистры"), который включает
оба из них. Иначе вы получите неоптимальный код.
Вы должны также определить некоторую избыточную информацию относительно классов
регистров: для каждого класса : какие классы он содержит и какие содержатся в
нем; для каждой пары классов - самый большой класс, содержащийся в их
объединении.
Когда значение, занимающее несколько последовательных регистров ожидается в
некотором классе, все используемые регистры должны принадлежать этому классу.
Следовательно, классы регистров не могут использоваться для усиления требования
к регистровой паре, чтобы начаться регистром с четным номером. Способ
определять это требование - "HARD_REGNO_MODE_OK".
Классы регистров, используемых как операнды побитового "И" или команд сдвига,
имеют специальное требование: каждый такой класс должен иметь для каждого типа
с фиксированной точкой, подкласс, регистры которого могут передать этот тип в
память или из нее. Например, на некоторых машинах, операции для однобайтовых
значений ("QImode") ограничены конкретными регистрами. Когда это так, каждый
класс регистров, который используется в побитовом "И" , или командах сдвига,
должен иметь подкласс, состоящий из регистров, из которых однобайтовые значения
могут быть загружены или сохранены. Так что "PREFERRED_RELOAD_CLASS" всегда
имеет значение для возврата.
"enum reg_class"
Перечислимый тип, который должен быть определен всеми именами классов регистров
как перечисляемыми значениями. "NO_REGS" должен быть первым. "ALL_REGS" должен
быть последним классом регистров, сопровождаемым еще одним значением,
"LIM_REG_CLASSES", которое не является классом регистров, но сообщает о
- 202 -
количестве классов.
Каждый класс регистров имеет номер, который является значением приведения имени
класса, к типу "int". Номер служит индексом во многих таблицах, описанных
ниже.
"N_REG_CLASSES"
Число различных классов регистров, определенных следующим образом:
#define N_REG_CLASSES (int) LIM_REG_CLASSES
"REG_CLASS_NAMES"
Инициализатор, содержащий имена классов регистров как строковые константы C.
Эти имена используются при писании некоторых отладочных дампов.
"REG_CLASS_CONTENTS"
Инициализатор, содержащий оглавление классов регистров как целые числа,
являющиеся битовыми масками. N-ое целое число определяет оглавление класса N.
Целочисленная маска MASK интерпретируется так : регистр R находится в классе,
если "MASK & (1 << R)" не равно нулю.
Когда машина имеет больше чем 32 регистра, целого числа ("int") не хватает.
Тогда целые числа заменяются под-инициализаторами, группами по несколько целых
чисел. Каждый под-инициализатор должен подходить как инициализатор для типа
"HARD_REG_SET", который определен в "hard-reg-set.h".
"REGNO_REG_CLASS (REGNO)"
Выражение C, значение которого - класс регистров, содержащий REGNO аппаратного
регистра. В общем случае имеется более, чем один такой класс; выберите
"минимальный" (такой что никакой меньший класс не содержит данный регистр).
"BASE_REG_CLASS"
Макрокоманда, определение которой - имя класса которому должен принадлежать
правильный базовый регистр . Базовый регистр - тот, который используется в
адресе, являющемся значением этого регистра плюс смещение.
"INDEX_REG_CLASS"
Макрокоманда, определение которой - имя класса, которому должен принадлежать
- 203 -
индексный регистр . Индексный регистр - тот, который используется в адресе, где
значение его умножается на масштабирующий коэффициент или прибавляется к
другому регистру (или добавляется к смещению).
"REG_CLASS_FROM_LETTER (CHAR)"
Выражение C, которое определяет машинно-зависимые символы ограничения операнда
для классов регистров. Если CHAR - такой символ, значением должен быть класс
регистров, соответствующий ему. Иначе, значение должно быть "NO_REGS". Символ
регистра "r", соответствующий классу "GENERAL_REGS", не будет передан этой
макрокоманде; вы не должны обрабатывать его.
"REGNO_OK_FOR_BASE_P (NUM)"
Выражение C, которое отлично от нуля, если регистр номер NUM подходит для
использования как базовый регистр в адресах операндов. Это может быть как
подходящий аппаратный регистр, так и псевдорегистр, в котором был распределен
аппаратный регистр.
"REGNO_OK_FOR_INDEX_P (NUM)"
Выражение C, которое отлично от нуля если регистр номер NUM подходит для
использования как индексный регистр в адресах операндов. Это может быть как
подходящий аппаратный регистр, так и псевдорегистр, в котором был распределен
аппаратный регистр.
Различие между индексным регистром и базовым регистром в том, что индексный
регистр может масштабироваться. Если адрес включает сумму двух регистров, ни
один из них не масштабирован, то любой может быть помечен как "основной", а
другой как "индексный"; но какая бы пометка не использовалась, она должна
удовлетворять ограничениям машины, регистры которой могут служить только в
определенных местах. Транслятор пробует оба способа, ища правильный, и
перезагружает один или оба регистра, только если никакой способ пометки не
проходит.
"PREFERRED_RELOAD_CLASS (X, CLASS)"
Выражение C, которое налагает дополнительные ограничения на класс регистров,
если необходимо копировать значение X в регистр класса CLASS. Значение - класс
регистров; возможно CLASS, или другой, меньший класс. На многих машинах,
безопасно следующее определение :
- 204 -
#define PREFERRED_RELOAD_CLASS (X, CLASS) CLASS
Иногда возвращение более ограниченного класса приводит к лучшему коду.
Например, на 68000, когда X - целочисленная константа, которая находится в
диапазоне для команды "moveq", значение этой макрокоманды - всегда "DATA_REGS",
так как CLASS включает регистры данных. Требование регистра данных гарантирует,
что "moveq" будет использоваться.
Если X - "const_double", возвращая "NO_REGS" вы может превратить X в константу
в памяти. Это полезно на некоторых машинах, где непосредственные вещественные
значения не могут быть загружены в некоторые типы регистров.
"PREFERRED_OUTPUT_RELOAD_CLASS (X,CLASS)"
То же, что и "PREFERRED_RELOAD_CLASS", но для перезагрузки вывода вместо
перезагрузки ввода. Если Вы не определяете эту макрокоманду, по умолчанию
используется CLASS, неизмененный.
"LIMIT_RELOAD_CLASS (MODE, CLASS)"
Выражение C, которое налагает дополнительные ограничения на класс регистров,
чтобы использовать, когда необходимо держать значение типа MODE в
перезагружаемом регистре для которого обычно использовался бы класс CLASS.
В отличие от "PREFERRED_RELOAD_CLASS", эта макрокоманда должна использоваться,
когда имеются некоторые типы, которые просто не могут входить в некоторые
классы перезагрузки.
Значение - класс регистров; возможно CLASS, или другой, меньший класс.
Не определяйте эту макрокоманду, если целевая машина имеет ограничения, которые
требуют, чтобы макрокоманда делала нечто нетривиальное.
"SECONDARY_RELOAD_CLASS (CLASS, MODE, X)" "SECONDARY_INPUT_RELOAD_CLASS (CLASS,
MODE, X)" "SECONDARY_OUTPUT_RELOAD_CLASS (CLASS, MODE, X)"
Многие машины имеют некоторые регистры, которые не могут быть скопированы
непосредственно в память или из нее или даже из других типов регистров. Пример
- "MQ" регистр, который на большинстве машин может быть скопирован только через
регистр общего назначения, но не через память. Некоторые машины позволяют
- 205 -
копировать все регистры в память или из нее , но требуют рабочего регистра для
памяти (например, с символическим адресом на RT, и с некоторым символическим
адресом на Sparc при компиляции PIC). В некоторых случаях требуются и
промежуточный, и рабочий регистры.
Вы должны определить эти макрокоманды, чтобы указать для фазы перезагрузки, что
может потребоваться распределить по крайней мере один регистр для перезагрузки
в дополнение к регистру, содержащему данные. А именно, если при копировании X в
регистр класса CLASS типа MODE требуется промежуточный регистр, вы должны
определить "SECONDARY_INPUT_RELOAD_CLASS", чтобы возвратить самый большой класс
регистров, все регистры которого могут использоваться как промежуточные или
рабочие регистры.
Если при копировании регистра класса CLASS типа MODE в X требуется
промежуточный или рабочий регистр, "SECONDARY_OUTPUT_RELOAD_CLASS" должна быть
определена, чтобы возвратить самый большой требуемый класс регистров. Если
требования для перезагрузок ввода и вывода одни и те же, то должна
использоваться макрокоманда "SECONDARY_RELOAD_CLASS" вместо того, чтобы
определять обе макрокоманды совершенно одинаковыми.
Значения, возвращенные этими макрокомандами - часто "GENERAL_REGS".
Возвращается "NO_REGS", если не требуется никакого запасного регистра; то есть,
если X может быть непосредственно скопирован в или из регистра класса CLASS
типа MODE, не требуя рабочего регистра. Не определяйте эту макрокоманду, если
она всегда возвращала бы "NO_REGS".
Если требуется рабочий регистр (с промежуточным регистром или без него), вы
должны определить образцы для "reload_inM" или "reload_outM", как требуется
(*обратите внимание на Стандартные Имена::.. Эти образцы, которые будут обычно
выполняться с "define_expand", должны быть подобны образцам "movM", за
исключением того, что операнд 2 - рабочий регистр.
Определите ограничения для регистра перезагрузки и рабочего регистра, которые
содержат единственный класс регистров. Если первоначальный регистр перезагрузки
(класс которого - CLASS) может встречать ограничение, данное в образце,
значение, возвращенное этими макрокомандами используется для класса рабочего
регистра. Иначе, требуются два дополнительных регистра перезагрузки. Их классы
получены из ограничений в образце insn.
- 206 -
X может быть псевдорегистром или подрегистром псевдорегистра, который может
быть в аппаратном регистре или в памяти. Используйте "true_regnum", чтобы
выяснить это; она возвратит -1, если псевдорегистр находится в памяти или номер
аппаратного регистра, если псевдорегистр находится в регистре.
Эти макрокоманды не должны использоваться в случае, если специфический класс
регистров может быть только скопирован в память или в другой класс регистров. В
этом случае, вторичные регистры перезагрузки необязательны и не помогают.
Вместо этого для выполнения копирования должен использоваться стек, и образец
"movM" должен использовать память как место промежуточного хранения. Это часто
происходит между регистрами с плавающей точкой и регистрами общего
назначения.
"SECONDARY_MEMORY_NEEDED (CLASS1, CLASS2, M)" Некоторые машины имеют свойство,
что некоторые регистры не могут быть скопированы в некоторые другие регистры
без использования памяти. Определите эту макрокоманду на этих машинах, чтобы
это было выражением C, которое отлично от нуля, если объекты типа M в регистрах
CLASS1 могут быть скопированы в регистры класса CLASS2, только сохраняя
регистр CLASS1 в память и загружая ячейку памяти в регистр класса CLASS2.
Не определяйте эту макрокоманду, если значение было бы всегда ноль.
"SECONDARY_MEMORY_NEEDED_RTX (MODE)"
Обычно, когда "SECONDARY_MEMORY_NEEDED" определена, транслятор распределяет
слот (slot) стека для ячейки памяти, необходимой для копии регистра. Если эта
макрокоманда определена, то транслятор вместо этого использует ячейку памяти,
определенную этой макрокомандой.
Не определяйте эту макрокоманду, если Вы не определили
"SECONDARY_MEMORY_NEEDED".
"SECONDARY_MEMORY_NEEDED_MODE (MODE)"
Когда транслятор нуждается во вторичной ячейке памяти, чтобы копировать один
регистр типа MODE в другой, он обычно распределяет достаточное количество
памяти, чтобы хранить "BITS_PER_WORD" битов и выполняет операции
сохранения/загрузки в типе шириной в много битов, класс которого - MODE.
- 207 -
Это пойдет на большинстве машин, потому что это гарантирует, что все биты
регистра скопированы, и предотвращает доступ к регистрам в более узкого (по
ширине) типа, что некоторые машины запрещают для регистров с плавающей
точкой.
Однако, это заданное по умолчанию поведение неправильно на некоторых машинах,
таких как, например, DEC Alpha, которые сохраняют короткие целых числа в
регистрах с плавающей точкой не так, как в целочисленных регистрах. На этих
машинах, заданное по умолчанию расширение не будет работать правильно, и вы
должны определить эту макрокоманду, чтобы подавить это расширение в некоторых
случаях. См. подробности в файле "alpha.h".
Не определяйте эту макрокоманду, если Вы не определяете
"SECONDARY_MEMORY_NEEDED" или если расширение MODE до типа, который имеет
ширину "BITS_PER_WORD" бит, работает правильно на вашей машине.
"SMALL_REGISTER_CLASSES"
Обычно транслятор избегает выбирать регистры, которые были явно упомянуты в
rtl, как spill-регистры (те, которые используются, для передачи параметров и
возвращаемого значения). Однако, некоторые машины имеют слишком мало регистров
некоторых классов, что не хватит регистров, чтобы использовать их как
spill-регистры, если это было выполнено.
Определите "SMALL_REGISTER_CLASSES" на этих машинах. Когда она определена,
транслятор позволяет регистрам, явно используемым в rtl, использоваться как
spill-регистры, но предотвращает расширение времени существования этих
регистров.
Определять эту макрокоманду всегда безопасно, но если вы без необходимости
определяете ее, то в некоторых случаях вы уменьшите возможности оптимизации,
которая может выполняться. Если вы не определяете эту макрокоманду, когда это
требуется, что транслятор исчерпает все spill-регистры и напечатает сообщение о
фатальной ошибке. На большинстве машин, вы не должны определять эту
макрокоманду.
"CLASS_LIKELY_SPILLED_P (CLASS)"
Выражение C, значение которого отлично от нуля если псевдорегистры, которые
были назначены регистрам класса CLASS, могут быть использованы как
- 208 -
spill-регистры, потому что регистры класса CLASS необходимы для
spill-регистров.
Значение по умолчанию этой макрокоманды равно 1, если CLASS имеет ровно один
регистр, иначе - ноль . На большинстве машин, это значение по умолчанию должно
использоваться. Просто определите эту макрокоманду как некоторое другое
выражение, если псевдорегистры, распределенные "local-alloc.c", заканчиваются в
памяти, потому что их аппаратные регистры были необходимы для spill-регистров.
Если эта макрокоманда возвращает не ноль для этих классов, эти псевдорегистры
будут распределены "global.c", который знает, как перераспределить
псевдорегистр в другой регистр. Если не имелось бы другого регистра для
перераспределения, вам не следовало бы изменять определение этой макрокоманды,
так как единственный эффект такого определения был бы замедление распределения
регистров.
"CLASS_MAX_NREGS (CLASS, MODE)"
Выражение C для максимального числа последовательных регистров класса CLASS,
требуемых для хранения значения типа MODE.
Макрокоманда тесно связана с макрокомандой "HARD_REGNO_NREGS". Фактически,
значение макрокоманды "CLASS_MAX_NREGS (CLASS, MODE)" должно быть максимальным
значением "HARD_REGNO_NREGS(REGNO,MODE)" по всем значениям REGNO в классе
CLASS.
Эти макрокоманды помогают управлять обработкой значений в много слов в процессе
перезагрузки.
"CLASS_CANNOT_CHANGE_SIZE"
Если определена, то это выражение C для класса, который содержит регистры, к
которым транслятор должен всегда обращаться в типе, размер которого - тот же,
что у типа, в котором он загрузил регистр.
Для примера, загрузка 32-битного целого числа или числа с плавающей точкой в
регистры с плавающей точкой на Альфе расширяет их до 64 битов. Следовательно,
загрузка 64-битного объекта и затем сохранение его как 32-битного не сохраняют
младшие 32 бита, как было бы в случае нормального регистра. Так что "alpha.h"
определяет эту макрокоманду как "FLOAT_REGS".
- 209 -
Три других специальных макрокоманды описывают, какие операнды пригодны для
каких символов ограничения.
"CONST_OK_FOR_LETTER_P (VALUE, C)"
Выражение C, которое определяет машинно-зависимые символы ограничения операнда
которые определяют специфические диапазоны целочисленных значений. Если C -
один из этих символов, выражение должно проверить, что VALUE, целое число,
находится в соответствующем диапазоне и возвратить 1 если это так, иначе - 0.
Если C - не является одним из этих символов, то значение должно быть 0
независимо от VALUE.
"CONST_DOUBLE_OK_FOR_LETTER_P (VALUE, C)"
Выражение C, которое определяет машинно-зависимые символы ограничения операнда,
которые специфицируют конкретные диапазоны значений "const_double".
Если C - один из этих символов, выражение должно проверить, что VALUE, RTX кода
"const_double", находится в соответствующем диапазоне и возвратить 1 если так,
иначе - 0. Если C - не является одним из этих символов, значение должно быть 0
независимо от VALUE.
"const_double" используется для всех констант с плавающей точкой и для "DImode"
констант с фиксированной точкой. Данный символ может принимать любое или оба
вида значений. Он может использовать "GET_MODE " чтобы различить эти виды.
"EXTRA_CONSTRAINT (VALUE, C) "
Выражение C, которое определяет опциональные машинно-зависимые символы
ограничения, которые могут использоваться, чтобы выделять специфические типы
операндов, обычно ссылки в памяти, для целевой машины. Обычно эта макрокоманда
не будет определена. Если это требуется для специфической целевой машины, она
должна возвратить 1, если VALUE соответствует типу операнда, представленному
символом ограничения C. Если C не определен как дополнительное ограничение,
возвращенное значение должно быть 0 независимо от VALUE.
Например, на ROMP, команды загрузки не могут иметь вывод в r0, если ссылка в
памяти содержит символический адрес. Символ ограничения "Q" определен как
представляющий адрес в памяти, который *не* содержит символического адреса.
Альтернатива определена как "Q"-ограничение на ввод и "r"-ограничение на вывод.
Следующая альтернатива определяет "m" на вводе и класс регистров, который не
- 210 -
включает r0 на выводе.
6.6. Формат Стека и Соглашения о вызовах
Здесь описываются формат стека и соглашения о вызовах.
6.6.1.Основные Параметры Стека
Вот основные параметры стека.
"STACK_GROWS_DOWNWARD" ("Стек растет вниз")
Определите эту макрокоманду, если при вталкивании слова в стек указатель стека
перемещается на меньший адрес.
Когда мы говорим, "определите эту макрокоманду, если ...", это означает, что
транслятор проверяет эту макрокоманду только с "#ifdef", так что точное
определение не имеет значения.
"FRAME_GROWS_DOWNWARD" ("Кадр растет вниз")
Определите эту макрокоманду, если адреса локальных переменных имеют
отрицательные смещения относительно указателя кадра.
"ARGS_GROW_DOWNWARD" ("Аргументы растут вниз")
Определите эту макрокоманду, если последовательные параметры к функции занимают
уменьшающиеся адреса в стеке.
"STARTING_FRAME_OFFSET" ("Начальное смещение кадра")
Смещение от указателя кадра до первой локальной переменной, которая может быть
распределена.
Если "FRAME_GROWS_DOWNWARD", то смещение следующей локальной переменной слота
находится вычитанием длины первой локальной переменной из
"STARTING_FRAME_OFFSET". Или же прибавлением длины первой локальной переменной
к значению "STARTING_FRAME_OFFSET".
"STACK_POINTER_OFFSET" ("Смещение указателя стека")
Смещение от регистра указателя стека до первого расположения, в которое
- 211 -
помещены поступающие параметры. Если не определена, то используется значение по
умолчанию - 0. Это - верное значение для большинства машин.
Если "ARGS_GROW_DOWNWARD", то это - смещение к расположению над первым
расположением, в которое помещены поступающие параметры.
"FIRST_PARM_OFFSET (FUNDECL)" ("Смещение первого параметра")
Смещение регистра-указателя параметров до адреса первого параметра. На
некоторых машинах это может зависеть от типа функции.
Если "ARGS_GROW_DOWNWARD", это - смещение расположения над адресом первого
параметра.
"STACK_DYNAMIC_OFFSET (FUNDECL)" ("Динамическое смещение стека")
Смещение от регистра указателя стека до того, что динамически размещено в
стеке, например "alloca".
Значение по умолчанию для этой макрокоманды - "STACK_POINTER_OFFSET" плюс длина
параметров. Значение по умолчанию правильно для большинства машин. См.
подробности в "function.c".
"DYNAMIC_CHAIN_ADDRESS (FRAMEADDR)" ("Динамический адрес цепи")
Выражение C, значение которого - RTL-представление адреса в кадре стека, где
сохранен указатель на кадр вызывающей функции. Имейте в виду, что FRAMEADDR -
RTL-выражение для адреса самого кадра стека.
Если Вы не определяете эту макрокоманду, по умолчанию должно быть возвращено
значение FRAMEADDR - то есть адрес кадра стека, или адрес слова стека, которое
указывает на предыдущий кадр.
"SETUP_FRAME_ADDRESSES ()"
Если определена, то это выражение C, которое производит машинно - специфический
код установки стека в такое положение, чтобы можно было обращаться к
произвольным кадрам. Например, на Sparc, мы должны сбросить все окна регистров
в стек прежде, чем мы можем обращаться к произвольным кадрам. Эту макрокоманду
редко надо определять.
"RETURN_ADDR_RTX (COUNT, FRAMEADDR)"
- 212 -
Выражение C, значение которого - RTL-представление значения адреса возврата для
COUNT кадра, полученное из текущего кадра. FRAMEADDR - указатель COUNT кадра,
или указатель на COUNT-1 кадра, если "RETURN_ADDR_IN_PREVIOUS_FRAME"
определена.
"RETURN_ADDR_IN_PREVIOUS_FRAME"
Определите ее, если к адресу возврата конкретного кадра стека обращаются из
указателя предыдущего кадра стека.
6.6.2. Регистры, Адресующие Кадр стека
Обсуждение регистров, адресующих кадр стека.
"STACK_POINTER_REGNUM"
Номер регистра указателя стека, который должен также быть фиксированным
регистром из "FIXED_REGISTERS". На большинстве машин, аппаратные средства
определяют, какой это регистр.
"FRAME_POINTER_REGNUM"
Номер регистра указателя кадра, который используется, чтобы обратиться к
автоматическим переменным в кадре стека. На некоторых машинах, аппаратные
средства определяют, какой это регистр. На других машинах вы можете выбирать
любой регистр, какой пожелаете, для этой цели.
"HARD_FRAME_POINTER_REGNUM"
На некоторых машинах смещение между указателем кадра и начальным смещением
автоматических переменных неизвестно, пока не выполнено распределение регистров
(например, потому что сохраненные регистры - между этими двумя расположениями).
На таких машинах определите "FRAME_POINTER_REGNUM" как номер специального,
фиксированного регистра, который должен использоваться внутри (internally),
пока смещение не станет известным, и определите "HARD_FRAME_POINTER_REGNUM" как
фактический номер аппаратного регистра, используемого для указателя кадра.
Вы должны определить эту макрокоманду только в очень редком случае, когда
невозможно вычислить смещение между указателем кадра и автоматическими
переменными, пока распределение регистров не было завершено. Когда эта
макрокоманда определена, вы должны также указать в вашем определении
- 213 -
"ELIMINABLE_REGS", как заменить "FRAME_POINTER_REGNUM" на
"HARD_FRAME_POINTER_REGNUM" или на "STACK_POINTER_REGNUM".
Не определяйте эту макрокоманду, если она совпадала бы с
"FRAME_POINTER_REGNUM".
"ARG_POINTER_REGNUM"
Номер arg регистра указателя, который используется для обращения к списку
параметров функции. На некоторых машинах, это - то же, что и регистр указателя
кадра. На некоторых машинах, аппаратные средства определяют, какой это регистр.
На других машинах вы можете выбрать любой регистр, который пожелаете, для этой
цели. Если это - не тот же самый регистр, что и регистр указателя кадра, то Вы
должны отметить его как фиксированный регистр из "FIXED_REGISTERS", или принять
меры для устранения его (*обратите внимание на Удаление::.).
"STATIC_CHAIN_REGNUM", "STATIC_CHAIN_INCOMING_REGNUM"
Номера регистров, используемые для передачи указателя статической цепи функции.
Если используются окна регистров, то это номер регистра, видимый вызываемой
функцией - "STATIC_CHAIN_INCOMING_REGNUM", в то время как номер регистра,
видимый вызванной функцией - "STATIC_CHAIN_REGNUM". Если эти регистры одни и те
же, то "STATIC_CHAIN_INCOMING_REGNUM" не должна быть определена.
Статический регистр цепи не должен быть фиксированным регистром.
Если статическая цепочка передана в память, эти макрокоманды не должны быть
определены; вместо этого должны быть определены следующие две макрокоманды.
"STATIC_CHAIN"
"STATIC_CHAIN_INCOMING"
Если статическая цепочка передана в память, эти макрокоманды обеспечивают rtx
данных выражений "mem", которые обозначают, где они сохранены. "STATIC_CHAIN" и
"STATIC_CHAIN_INCOMING" делают ячейки видимыми как вызывающим, так и вызываемым
функциям, соответственно. Часто первая будет при смещении из указателя стека и
последняя при смещении из указателя кадра.
Переменные "stack_pointer_rtx", "frame_pointer_rtx" и "arg_pointer_rtx" будут
проинициализированы до использования этих макрокоманд и должны использоваться
для обращения к этим структурам.
- 214 -
Если статическая цепочка передана в регистр, две предыдущих макрокоманды должны
быть определены вместо этих.
6.6.3. Удаление Указателя Кадра и Указателя Аргументов
Что касается удаления указателя кадра и указателя аргументов.
"FRAME_POINTER_REQUIRED"
Выражение C, которое отлично от нуля, если функция имеет и использует указатель
кадра. Это выражение вычисляется в процессе перезагрузки. Если значение отлично
от нуля, функция будет иметь указатель кадра.
Это выражение может в принципе исследовать текущую функцию и решить, опираясь
на факты, но на большинстве машин, достаточно констант 0 и 1. Используйте 0,
когда машина позволяет коду быть сгенерированному без указателя кадра, и это
сохраняет немного времени и места. Используйте 1, когда нет преимущества от
отсутствия указателя кадра.
В некоторых случаях, транслятор не знает, как произвести правильный код без
указателя кадра. Транслятор распознает такие случаи и автоматически делает
указатель кадра для функции, независимо от значения "FRAME_POINTER_REQUIRED".
Не надо беспокоиться о них.
В функции, которой не требуется указатель кадра, регистр указателя кадра может
быть распределен для обычного использования, если Вы не отметили его как
фиксированный регистр. Для дополнительной информации об этом ищите
"FIXED_REGISTERS".
"INITIAL_FRAME_POINTER_OFFSET (DEPTH-VAR)"
Оператор C сохранения в переменной DEPTH-VAR разности между указателем кадра и
указателем стека сразу после пролога функции. Значение было бы вычислено из
информации типа результата "get_frame_size()" и таблиц регистров
"regs_ever_live" и "call_used_regs".
Если "ELIMINABLE_REGS" определена, эта макрокоманда не будет использоваться и
не должна быть определена. Иначе она должна быть определена, даже если
- 215 -
"FRAME_POINTER_REQUIRED" определена как тождественно истинный; в этом случае,
вы можете присвоить DEPTH-VAR любое значение.
"ELIMINABLE_REGS"
Если определена, эта макрокоманда определяет таблицу пар регистров,
используемую для устранения ненужных регистров, которые указывают на кадр
стека. Если не определена, единственное удаление, которое транслятор пытается
сделать - замена ссылок на указатель кадра на ссылки на указатель стека.
Определение этой макрокоманды состоит в списке инициализаций структур, каждая
из которых определяет оригинальный регистр и регистр замены.
На некоторых машинах позиция указателя параметров неизвестна, пока трансляция
не завершена. В таком случае, отдельный аппаратный регистр должен
использоваться как указатель параметров. Этот регистр может быть удален путем
замены на указатель кадра или на указатель параметров, в зависимости от того,
удален или не удален указатель кадра.
В этом случае, вы могли бы определить:
#define ELIMINABLE_REGS \ {{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ {FRAME_POINTER_REGNUM,
STACK_POINTER_REGNUM}}
Обратите внимание, что удаление указателя параметров с указателем стека
определено сначала, так как это удаление предпочтительно.
"CAN_ELIMINATE (FROM-REG, TO-REG)"
Выражение C, которое возвращает значение, отличное от нуля, если транслятору
позволяется пробовать заменять регистр номер FROM-REG на регистр номер TO-REG.
Эта макрокоманда должна быть определена только если определена
"ELIMINABLE_REGS", и будет обычно константной 1, так как большинство случаев,
предотвращающих удаление регистра - вещи о которых транслятор уже знает.
"INITIAL_ELIMINATION_OFFSET (FROM-REG, TO-REG, OFFSET-VAR)" Эта макрокоманда
подобна "INITIAL_FRAME_POINTER_OFFSET". Она определяет начальное различие между
парой регистров. Макрокоманда должна быть определена, если "ELIMINABLE_REGS"
определена.
- 216 -
"LONGJMP_RESTORE_FROM_STACK"
Определяйте эту макрокоманду, если функция "longjmp" восстанавливает регистры
из кадра стека, а не из сохраненных "setjmp". Некоторые величины не должны
сохраняться в регистрах во время обращения к "setjmp" на таких машинах.
6.6.4. Передача Аргументов Функции через Стек
Макрокоманды в этом разделе управляют тем, как параметры передаются в стек. См.
следующий раздел о других макрокомандах, которые управляют передачей конкретных
типов параметров через регистры.
"PROMOTE_PROTOTYPES"
Определяйте эту макрокоманду, если параметр, объявленный в прототипе как целый
тип меньший, чем "int", должен фактически быть передан как "int". В дополнение
к избежанию ошибок в некоторых случаях несоответствия, это также приводит к
лучшему коду на некоторых машинах.
"PUSH_ROUNDING (NPUSHED)"
Выражение C, которое является числом байт, фактически переданных через стек,
когда команда пытается помещать NPUSHED байт.
Если целевая машина не имеет команды push ("поместить в стек"), не определяйте
эту макрокоманду. Это указывает GNU CC использовать альтернативную стратегию:
распределить весь блок параметров и затем сохранить параметры в стеке.
На некоторых машинах, определение
#define PUSH_ROUNDING (BYTES) (BYTES)
будет в самый раз. Но на других машинах команды, которые помещают в стек один
байт, фактически помещает два байта, поддерживая выравнивание. Тогда
определение должно быть :
#define PUSH_ROUNDING (BYTES) (((BYTES) + 1) и ~1)
"ACCUMULATE_OUTGOING_ARGS"
Если определена, то максимальный размер области, требуемой для параметров,
- 217 -
будет вычислен и будет помещен в переменную
"current_function_outgoing_args_size". Ничего не будет помещаться на стек при
каждом вызове; вместо этого пролог функции должен увеличить размер кадра стека
на это количество байт.
Нельзя определять "PUSH_ROUNDING" и "ACCUMULATE_OUTGOING_ARGS" одновременно.
"REG_PARM_STACK_SPACE (FNDECL)"
Определите эту макрокоманду, если функции рассчитывают, что место в стеке
распределено для параметров даже когда их значения переданы в регистрах.
Значение этой макрокоманды - размер в байтах области, зарезервированной для
параметров, переданных в регистрах функции, представляемой FNDECL.
Это место может быть распределено вызывающей функцией, или быть частью
машинно-зависимого кадра стека: "OUTGOING_REG_PARM_STACK_SPACE" говорит, кто
его распределит.
"MAYBE_REG_PARM_STACK_SPACE" "FINAL_REG_PARM_STACK_SPACE(CONST_SIZE, VAR_SIZE)"
Определите эти макрокоманды в дополнение к описанной выше, если функции могут
распределять место в стеке для параметров даже когда их значения переданы через
регистры. Они должны использоваться, когда место в стеке, распределенное для
параметров в регистрах - не простая константа, независимая от декларации
функции.
Значение первой макрокоманды - размер в байтах области, которую мы должны
первоначально считать зарезервированной для параметров, переданных в
регистрах.
Значение второй макрокоманды - фактический размер в байтах области, которая
будет зарезервирована для параметров, переданных в регистрах. У нее два
параметра: целое число, представляющее число байт параметров фиксированного
размера, и дерево, представляющее число байт параметров переменного размера,
переданных в стек.
Когда эти макрокоманды определены, "REG_PARM_STACK_SPACE" будет вызываться
только для функций libcall, текущей функции и для функции, вызываемой, когда о
ней известно, что такое место в стека должно быть распределено. В любом случае
- 218 -
это значение может быть легко вычислено.
При выяснении, нуждается ли вызываемая функция в таком пространстве стека, и
сколько надо резервировать, GNU CC использует эти две макрокоманды вместо
"REG_PARM_STACK_SPACE".
"OUTGOING_REG_PARM_STACK_SPACE"
Определите ее, если вызывающая функция обязана распределить область,
зарезервированную для параметров, переданных в регистрах.
Если "ACCUMULATE_OUTGOING_ARGS" определена, эта макрокоманда управляет, не
превосходит ли место для параметров значение
"current_function_outgoing_args_size".
"STACK_PARMS_IN_REG_PARM_AREA"
Определите эту макрокоманду, если "REG_PARM_STACK_SPACE" определена, но
стековые параметры не пропускают область, определенную ею.
Обычно, когда параметр не передан в регистрах, он помещен в стек за
"REG_PARM_STACK_SPACE" областью. Определение этой макрокоманды подавляет такое
поведение и заставляет параметр передаваться в стек на естественное место.
"RETURN_POPS_ARGS (FUNDECL, FUNTYPE, STACK-SIZE)" Выражение C, которое должно
указать размер в байтах параметров функции, которые она выталкивает при
возврате, или 0, если функция не выталкивает параметры, а вызывающая функция
должна следовательно вытолкнуть их после возврата из функции.
FUNDECL - переменная C, значение которой - узел дерева, который описывает
рассматриваемую функцию. Обычно это - узел типа "FUNCTION_DECL", который
описывает декларацию функции. Из нее можно получить DECL_MACHINE_ATTRIBUTES для
функции.
FUNTYPE - переменная C, значение которой - узел дерева, который описывает
рассматриваемую функцию. Обычно это - узел типа "FUNCTION_TYPE", который
описывает тип функции. Из нее можно получить типы значения и параметров (если
они есть).
Когда рассматривается обращение к библиотечной функции, FUNTYPE будет содержать
- 219 -
идентификатор узла для библиотечной функции. Таким образом, если нужно
различить библиотечные функции, вы можете сделать это по их именам. Обратите
внимание, что "библиотечная функция" в этом контексте означает арифметическую
функцию, имя которой известно только транслятору и не упомянуто в компилируемом
C-коде.
STACK-SIZE - число байтов параметров, переданных в стек. Если передано
переменное число байт, это - ноль, и выталкивание параметров всегда будет
обязанностью вызывающей функции.
На Vax все функции всегда выталкивают свои аргументы, так что определение этой
макрокоманды - STACK-SIZE. На 68000, согласно стандартному соглашению о
вызовах, никакие функции не выталкивают свои параметры, так что значение
макрокоманды - всегда 0 в этом случае. Но альтернативное соглашение о вызовах
имеет место, если функции, которые берут фиксированное число параметров,
выталкивают их, но другие функции (типа "printf") не выталкивают ничего
(вызывающая функция делает это за них). Когда используется это соглашение,
исследуется FUNTYPE для определения, берет ли функция фиксированное число
аргументов.
6.6.5. Передача Параметров В Регистрах
Этот раздел описывает макрокоманды, которые позволяют Вам управлять тем,
как различные типы параметров передаются в регистрах или как они размещаются
в стеке.
" FUNCTION_ARG (CUM, MODE, TYPE, NAMED) "
Выражение C, которые управлял тем, передается ли параметр функции
в регистре, и в каком регистре.
Параметры - CUM, который суммирует все предыдущие
параметры; MODE, машинный тип параметра; TYPE, тип данных
параметра как узел дерева или 0, если он не известен
(как бывает для библиотечных функций поддержки C); и NAMED, который
равен 1 для обычного парамета и 0 для безымянных параметров, что
соответствет " ... " в прототипе вызываемой функции.
- 220 -
Значение выражения должно быть или " reg " RTX для аппаратного регистра,
в котором следует передавать параметр, или ноль для передачи параметра в
стеке.
Для машин подобно Vax и 68000, где обычно все параметры
передаются в стеке, можно задавать в качестве определения
тождественный ноль.
Обычный способ заставить ANSI библиотеку " stdarg.h " работать на машине,
на которой некоторые параметры обычно передаются в регистрах, заключается
в передаче через стек безымянных параметров. Это достигается
возвращением " FUNCTION_ARG " 0 всякий раз, когда NAMED равно 0.
Вы можете использовать макрокоманду " MUST_PASS_IN_STACK (MODE, TYPE) " в
определении этой макрокоманды, чтобы определить, имеет ли этот параметр
тип, который должен передаваться в стеке. Если " REG_PARM_STACK_SPACE "
не определено и " FUNCTION_ARG " возвращает отличное от нуля число для
такого параметра, транслятор прервет работу. Если " REG_PARM_STACK_SPACE "
определено, параметр будет вычислен в стеке и затем загружен в регистр.
" FUNCTION_INCOMING_ARG (CUM, MODE, TYPE, NAMED) "
Определите эту макрокоманду, если целевая машина имеет " окна регистра ",
то есть регистр, в котором функция видит параметры, не обязательно
является тем, в котором вызывающая функция их передала.
Для таких машин " FUNCTION_ARG " вычисляет регистр, в котором
вызывающая функция передает значение, а " FUNCTION_INCOMING_ARG "
должен определяться подобным способом и сообщать вызываемой функции,
где получать параметры.
Если " FUNCTION_INCOMING_ARG " не определено, " FUNCTION_ARG " служит
для обеих целей.
" FUNCTION_ARG_PARTIAL_NREGS (CUM, MODE, TYPE, NAMED) "
Выражение C для числа слов, в начале параметра, которые должны быть
помещены в регистры. Значение должно быть нулем для
параметров, которые полностью передаются в регистрах или
полностью помещаются в стек.
- 221 -
На некоторых машинах определенные параметры должны передаваться частично
в регистрах и частично в памяти. На этих машинах обычно первые N слов
параметров передаются в регистрах, а остальные - в стеке. Если параметр,
состоящий из нескольких слов (" double " или структура), пересекает эту
границу, его первые несколько слов должны передаваться в регистрах, а
остальные - в стеке. Эта макрокоманда сообщает транслятору, когда это
происходит и сколько слов должен идти в регистры.
" FUNCTION_ARG " для этих параметров должна возвращать первый
регистр, который должен использоваться вызывающей функцией для этого
параметра; аналогично " FUNCTION_INCOMING_ARG " - для вызываемой функции.
" FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED) "
Выражение C, которое указывает, когда параметр должен передаваться по
ссылке. Если оно отлично от нуля для параметра, в памяти делается копия
этого параметра и указатель на параметр передается вместо него самого.
Указатель передается любым способом, соответствующий передаче указателя
на этот тип.
На машинах, где " REG_PARM_STACK_SPACE " не определено,
определения этой макрокоманды может выглядеть так:
#define FUNCTION_ARG_PASS_BY_REFERENCE\
(CUM, MODE, TYPE, NAMED) \
MUST_PASS_IN_STACK (MODE, TYPE)
" FUNCTION_ARG_CALLEE_COPIES (CUM, MODE, TYPE, NAMED) "
Если определено, выражение C, которое указывает когда задача создания
копий параметров, переданных по невидимой ссылке, возлагается на
вызываемую функцию. Обычно вызывающая функция делает копию и передает ее
адрес вызываемой подпрограмме. Когда FUNCTION_ARG_CALLEE_COPIES определена
и отлична от нуля, вызывающая функция не делает копии. Вместо этого она
передает указатель на "настоящее" значение. Вызываемая функция не должна
изменять это значение. Если можно определить, будет ли значение
изменяться, делать копию не обязательно; иначе копия должна быть сделана.
" CUMULATIVE_ARGS "
Тип C для объявления переменной, которая используется как первый
- 222 -
параметр " FUNCTION_ARG " и другие связанных значений. Для некоторых
целевых машин тип " int " подходит для этой цели и может хранить
количество байтов.
Нет необходимости записывать в " CUMULATIVE_ARGS " что-либо о параметрах,
которые передаются через стек. Транслятор другие переменные, чтобы
следить за этим. Для целевых машин, на которых все параметры передаются
через стек, нет необходимости записывать что-либо в " CUMULATIVE_ARGS ";
однако, структура данных должна существовать и не должна быть пустой,
так что используйте " int ".
" INIT_CUMULATIVE_ARGS (CUM, FNTYPE, LIBNAME) "
Оператор C (без точки с запятой) для инициализации переменной CUM
для состояния в начале списка параметров. Переменная
имеет тип " CUMULATIVE_ARGS ". Значение FNTYPE - узел дерева
для типа данных функции, которая получит параметры, или 0
если аргументы - для библиотечной функции поддержки транслятора.
При обработке обращения к библиотечной функции поддержки транслятора,
LIBNAME определяет, к какой именно. Это - " symbol_ref " rtx, который
содержит имя функции как строку. LIBNAME равно 0, если обрабатывается
обычное обращение к функции C. Таким образом, каждый раз
эта макрокоманда вызывается, или LIBNAME, или FNTYPE отличны от нуля,
но не оба сразу.
" INIT_CUMULATIVE_INCOMING_ARGS (CUM, FNTYPE, LIBNAME) "
Подобно " INIT_CUMULATIVE_ARGS ", но перекрывает ее для целей
нахождения параметров для компилируемой функции. Если это
макрокоманда не определена, используется " INIT_CUMULATIVE_ARGS ".
Значение, передаваемое для LIBNAME - всегда 0, потому что библиотечные
подпрограммы со специальными соглашениями о вызовах никогда не
компилируются GNU CC. Параметр LIBNAME существует для симметрии с
" INIT_CUMULATIVE_ARGS ".
" FUNCTION_ARG_ADVANCE (CUM, MODE, TYPE, NAMED) "
Оператор C (без точки с запятой) для продвижения сумматора переменной
CUM мимо параметра в списке параметров. Значения
- 223 -
MODE, TYPE и NAMED описывают этот параметр. Как только это выполнено,
переменная CUM подходит для анализа *следующего*
параметра при помощи " FUNCTION_ARG ", и т.д.
Эта макро не должно ничего делать, если рассматриваемый параметр
передается в стеке. Транслятор знает, как отслеживать количество
свободного места в стеке, используемого для параметров, без
специальной помощи.
" FUNCTION_ARG_PADDING (MODE, TYPE) "
Если определено, выражение C, которое определяет, добавлять ли, и в каком
направлении дополнительное пространство параметру. Значение должно иметь
тип " enum direction ": " upward ", чтобы дополнить сверху,
" downward ", чтобы дополнить снизу, или " none ", чтобы запретить
дополнение.
*Размера* дополнения всегда в точночти достаточно, чтобы достигнуть
следующего кратного " FUNCTION_ARG_BOUNDARY "; эта макрокоманда не
управляет этим.
Эта макрокоманда имеет заданное по умолчанию определение, которое является
правильным для большинства систем. Для little-endian машин значение по
умолчанию - вверх. Для big-endian машин - вниз для параметра постоянного
размера, короче чем " int ", и вверх в противном случае.
" FUNCTION_ARG_BOUNDARY (MODE, TYPE) "
Если определено, выражение C, которое дает границу выравнивания, в
битах, параметра с указанными типом и типом. Если оно
не определено, для всех параметров используется " PARM_BOUNDARY ".
" FUNCTION_ARG_REGNO_P (REGNO) "
Выражение C, которое является отличным от нуля если REGNO - номер
аппаратного регистра, в котором иногда передаются параметры функций.
Это *не включает* неявные параметры типа статической цепочки и
адрес значения структуры. На многих машинах никакие регистры не могут
использоваться для этой цели, так как все параметры функций передаются
через стек.
- 224 -
6.6.6. Как Возвращаются Значения Скалярной Функции
В этом разделе обсуждаются макрокоманды, которые управляют возвращением
скаляров как значений - значений, которые помещаются в регистрах.
" TRADITIONAL_RETURN_FLOAT "
Определите эту макрокоманду, если " -traditional " не должно заставлять
функции, объявленные возвращающими " float ", преобразовывать значение в
" double ".
" FUNCTION_VALUE (VALTYPE, FUNC) "
Выражение C для создания RTX представления места, где a
функция возвращает значение типа данных VALTYPE. VALTYPE - узел дерева,
представляющий тип данных. Пишите " TYPE_MODE (VALTYPE) ", чтобы получить
машинный тип, используемый, чтобы представить этот тип. На многих машинах,
уместен только тип. (Фактически, на большинстве машин, скалярные
значения возвращаются в одном и том же месте независимо от типа).
Если " PROMOTE_FUNCTION_RETURN " определено, Вы должны применить те же
правила поддержки, что и определенные в " PROMOTE_MODE ", если VALTYPE -
скалярный тип.
Если точная вызываемая функция известна, FUNC - узел дерева
(" FUNCTION_DECL ") для нее; иначе, FUNC - нулевой указатель. Это
делает возможным использовать различные соглашения о возвращении
значений для отдельных функций, когда известны все обращения к ним.
" FUNCTION_VALUE " не используется для возврата значений составных типов
данных, потому что они возвращаются другим способом. См. ниже
" STRUCT_VALUE_REGNUM " и связанные с ним макрокоманды.
" FUNCTION_OUTGOING_VALUE (VALTYPE, FUNC) "
Определите эту макрокоманду, если целевая машина имеет " окна регистра ",
то есть регистр, в котором функция видит параметры, не обязательно
является тем, в котором вызывающая функция их передала.
Для таких машин " FUNCTION_VALUE " вычисляет регистр, в котором
- 225 -
вызывающая функция получит значение, а " FUNCTION_INCOMING_ARG "
должен определяться подобным способом и сообщать функции,
куда записать параметры.
Если " FUNCTION_OUTGOING_VALUE " не определена, " FUNCTION_VALUE "
служит для обеих целей.
" FUNCTION_OUTGOING_VALUE " не используется для возврата значений составных
типов данных, потому что они возвращаются другим способом. См. ниже
" STRUCT_VALUE_REGNUM " и связанные с ним макрокоманды.
" LIBCALL_VALUE (MODE) "
Выражение C, чтобы создать RTX представление места, где
библиотечная функция возвращает значение типа MODE. Если точная
вызываемая функция известна, FUNC - узел дерева
(" FUNCTION_DECL ") для нее; иначе, FUNC - нулевой указатель. Это
делает возможным использовать различные соглашения о возвращении
значений для отдельных функций, когда известны все обращения к ним.
Обратите внимание, что "библиотечная функция" в этом контексте означает
подпрограмму поддержки транслятора, используемую для выполнения
арифметической операции, чье имя специально указано транслятору и не
было упомянуто в компилируемом C коде.
Определение " LIBRARY_VALUE " не должно касаться составных типов данных,
потому что ни одна из библиотечных функций не возвращает таких типов.
" FUNCTION_VALUE_REGNO_P (REGNO) "
Выражение C, которое является отличным от нуля, если REGNO - номер
аппаратного регистра, в котором могут возвращаться значения вызываемой
функции.
Регистр, чей использование для возврата значений ограничено употреблением
как второго в пары (для значения типа " double ", например), не требуется
распознавать этой макрокомандой. Поэтому для большинства машин, достаточно
такого определения:
#define FUNCTION_VALUE_REGNO_P(N) ((N) == 0)
- 226 -
Если машина имеет окна регистра, так, чтобы вызывающая и
вызываемая функции использует различные регистры для возврата значения,
эта макрокоманда должна распознать только номера регистров вызывающей
функции.
" APPLY_RESULT_SIZE "
Определите эту макрокоманду, если " untyped_call " и " untyped_return "
требуется больше места, чем подразумевается " FUNCTION_VALUE_REGNO_P ", для
сохранения и восстановления произвольного возвращаемого значения.
6.6.7. Как Возвращаются Структурированные Значения
Когда тип значения функции - " BLKmode " (и в некоторых других случаях),
значение не возвращается согласно " FUNCTION_VALUE " (*См.:Возврат скаляров::.).
Вместо этого вызывающая функция передает адрес блока
памяти, в котором значение должно быть сохранено. Этот адрес называется
" структурным адресом значения ".
Этот раздел описывает, как управлять возвратом значений структур в памяти.
" RETURN_IN_MEMORY (TYPE) "
Выражение C, которое может запрещать возврат определенных значений
функции в регистрах, основываясь на типе значения. Ненулевое значение
требует возврата значения функции в памяти, так, как это делается для
больших структур. Здесь TYPE будет выражение C
типа " tree ", представляя тип данных значения.
Обратите внимание, что значения типа " BLKmode " должны явно
обрабатываться этой макрокомандой. Также, опция " -fpcc-struct-return "
имеет эффект независимо от этой макрокоманды. На большинстве систем можно
оставить макрокоманду неопределенной; тогда используется определение по
умолчанию, чье значение - константа 1 для значений ` BLKmode ' и 0 в
противном случае.
Не используйте эту макрокоманду, чтобы указать, что структуры и
объединения всегда должны возвращаться в памяти. Вы должны использовать
- 227 -
" DEFAULT_PCC_STRUCT_RETURN ", чтобы указать это.
" DEFAULT_PCC_STRUCT_RETURN "
Определите эту макрокоманду значением 1, если все возвращаемые значения
структур и объединений должны быть в памяти. Так как это приводит к более
медленному коду, это должно делаться, только если это необходимо для
совместимости с другими трансляторами или с ABI. Если Вы определяете эту
макрокоманду значением 0, то соглашения, используемые для возвращаемых
значений структур и объединений, определяются макрокомандой
" RETURN_IN_MEMORY ".
По умолчанию значение равно 1.
" STRUCT_VALUE_REGNUM "
Если адрес значения структуры передается в регистре, то
" STRUCT_VALUE_REGNUM " должно быть номером этого регистра.
" STRUCT_VALUE "
Если адрес значения структуры не передается в регистре, определите
" STRUCT_VALUE " как выражение, возвращающее RTX для места,
где адрес передается. Если возвращается 0, адрес
передается как "невидимый" первый параметр.
" STRUCT_VALUE_INCOMING_REGNUM "
При некоторых архитектурах - место, где вызываемая функция находит адрес
значения структуры - не то же самое место, куда его помещает вызывающая.
Это может быть из-за регистровых окон или из-за того, что
пролог функции перемещает его в другое место.
Если входящее расположение адреса значения структуры находится в
регистре, определите эту макрокоманду как номер регистра.
" STRUCT_VALUE_INCOMING "
Если входящее расположение - не регистр, то Вы должны определить
" STRUCT_VALUE_INCOMING " как выражение для RTX, содержащее место, в
котором вызываемая функция должна найти значение. Если она должна
найти его в стеке, определите ее как " mem ", которое ссылается на
указатель кадра. Определение нулем означает, что адрес
- 228 -
передается как "невидимый" первый параметр.
" PCC_STATIC_STRUCT_RETURN "
Определите эту макрокоманду, если обычное системное соглашение на целевой
машине для возврата структур и объединений заключается в том, что
вызываемая функция должна возвращать адрес статической переменной,
содержащей значение.
Не определите ее, если обычное системное соглашение системы заключается в
том, что вызывающая функция передает адрес подпрограмме.
Эта макрокоманда имеет эффект в типе " -fpcc-struct-return " , но не
играет роли, когда Вы используете тип " -freg-struct-return ".
6.6.8. Распределение Регистров с Сохранением
Если Вы разрешаете это, GNU CC может сохранять регистры на время обращений
к функции. Это делает возможным использование затираемых регистров для
содержания переменных, которые должны существовать и до, и после обращения.
" DEFAULT_CALLER_SAVES "
Определите эту макрокоманду, если обращения к функции на целевой машине
не сохраняют никаких регистров; другими словами, если " CALL_USED_REGISTERS "
имеет 1 для всех регистров. Эта макрокоманда допускает " -fcaller-saves "
по умолчанию. В конечном счете эта опция будет установлена по умолчанию на
всех машинах и как опция, так и эта макрокоманда будут удалены.
" CALLER_SAVE_PROFITABLE (REFS, CALLS) "
Выражение C, которое определяет, имеет ли смысл рассмотреть
размещение псевдорегистра в затираемом аппаратном регистре и
сохранять и восстанавливать его при каждом обращении к функции. Выражение
должно быть равно 1, если так следует поступать, и 0 - если нет.
Если Вы не определяете эту макрокоманду, используется значение по
умолчанию, который является разумным для большинства машин:
" 4 * CALLS < REFS ".
- 229 -
6.6.9. Вход и Выход из Функции
Этот раздел описывает макрокоманды, которые выводят код входа функции
("пролог") и выхода ("эпилог").
" FUNCTION_PROLOGUE (FILE, SIZE) "
Составной оператор C, который выводит код ассемблера для входа функции.
Пролог ответственен за установку кадра стека, инициализацию регистра
указателя кадра, сохранение регистров, которые должны быть сохранены
и распределение SIZE дополнительных байтов в памяти для локальных
переменных. SIZE - целое число. FILE - stdio поток (поток стандартного
ввода-вывода), в который должен выводиться код ассемблера.
Метка для начала функции не должна выводиться
этой макрокомандой. Это выполняется при запуске макрокоманды.
Чтобы определить, какие регистры сохранять, макрокоманда могут обратиться
к массиву " regs_ever_live ": элемент R отличен от нуля, если аппаратный
регистрир R используется где-нибудь внутри функции. Это подразумевает,
что пролог функции должен сохранить регистр R, если это - не один из
используемых обращением регистры. (" FUNCTION_EPILOGUE " должен
аналогично использовать " regs_ever_live ".)
На машинах, которые имеют " окна регистра ", код входа функции
не сохраняет в стеке регистры, которые находятся в окнах,
даже если предполагается, что они должны сохраняться при обращении к
функции; вместо этого требуется соответствующие шаги, чтобы "поместить" в
стек регистр, если какие-то регистры, не используемые в вызове,
используются в функции.
На машинах, где функции могут иметь или могут не иметь указатели кадра,
код входа функции должен изменяться соответственно; он должно
устанавливать указатель кадра, если он требуется, и не устанавливать в
противном случае. Чтобы определить нужен ли указатель кадра, макрокоманда
может обратиться к переменной " frame_pointer_needed ". Значение
переменной будет 1 во время выполнения фнукции, которая нуждается в
указателе кадра. * См.: Удаление::.
- 230 -
Код входа функции ответственен за распределение любого места в стеке,
требуемого для функции. Этот пространство состоит из областей,
перечисленных ниже. В большинстве случаев эти области, распределяются
в перечисленном порядке, причем названная последней область - самой
близкой к вершине стека (наименьший адрес, если " STACK_GROWS_DOWNWARD "
определено, и наибольший адрес, если не определено). Вы можете
использовать другой порядок для машины, если это будет удобнее
или требуется по причинам совместимости. За исключением случаев, когда
это требуется стандартом или отладчиком, не имеется никакой причины
считать, что распределение стека, используемое GCC, должно
согласовываться с используемым другими трансляторами для машины.
* Область из " current_function_pretend_args_size " байтов
неинициализированного места под первым параметром,
помещаемым в стек. (Оно не может быть в самом начале
распределяемой области стека, если последовательность вызовов
поместила в стек что-нибудь еще, кроме с параметров стека. Но
обычно на таких машинах ничто больше не помещается, потому что
пролог функции делает это сам.) Эта область используется на машинах,
где параметр может передаваться частично в регистрах и частично в
памяти, и, в некоторых случаях, чтобы поддерживать особенности
" varargs.h " и " stdargs.h ".
* Область памяти, используемая для сохранения некоторых регистров,
используемых функцией. Размер этой области, которая может также
включать место для таких вещей, как адрес возврата и указателей
предыдущие кадры стека, является машинно-специфическими и обычно
зависит от того, какие регистры используются в функции.
Машины с окнами регистра часто не требуют области сохранения.
* Область по крайней мере SIZE байтов, возможно округленных до
границы распределения, для содержания локальных переменных
функции. На некоторых машинах эта область и область сохранения
могут располагаться в противоположном порядке, так что область
сохранения будет ближе к вершине стека.
* Опционально, когда " ACCUMULATE_OUTGOING_ARGS " определено,
- 231 -
область из " current_function_outgoing_args_size " байтов для
использования для выходящих списков параметров функции. * См.:
Параметры Стека::.
Обычно, это необходимо для макрокоманд " FUNCTION_PROLOGUE " и
" FUNCTION_EPILOGUE ", особенно чтобы обработать функции листа.
Переменная C " leaf_function " отлична от нуля для такой функции.
" EXIT_IGNORE_STACK "
Определите эту макрокоманду как выражение C, которое отлично от нуля,
если команда возврата или эпилог функции игнорирует значение указателя
стека; другими словами, если безопасно удалить команду корректировки
указателя стека перед возвратом из функции.
Обратите внимание, что значение этой макрокоманды имеет значение только
для функций, которые поддерживают указатели кадра. Не бывает безопасно
удалить конечную корректировка стека в функции, которая не имеет
указателя кадра, и транслятор знает это независимо от
" EXIT_IGNORE_STACK ".
" FUNCTION_EPILOGUE (FILE, SIZE) "
Составной оператор C, который выводит код ассемблера для выхода
из функции. Эпилог ответственен за восстановление сохраненных регистров
и указателя стека на их значения, когда функция вызывалась,
и возвращения управления вызывающей функции. Эта макрокоманда имеет те же
параметры, что и макрокоманда " FUNCTION_PROLOGUE ", и регистры, которые
нужно восстановить, определяются из " regs_ever_live " и
" CALL_USED_REGISTERS " таким же образом.
На некоторых машинах имеется одиночная команда, которая делает всю
работу для возврата из функции. На этих машинах, задайте этой
команде имя " return " и не определяйте макрокоманду
" FUNCTION_EPILOGUE " вообще.
Не определяйте образец по имени " return ", если Вы хотите использовать
" FUNCTION_EPILOGUE ". Если Вы хотите, чтобы целевые переключатели
управляли тем, используются команды возврата или эпилоги,
определите образец " return " с условием проверки правильности, которое
- 232 -
соответственно проверяет целевые переключатели. Если условие
правильности образца " return " ложно, будут использоваться эпилоги.
На машинах, где функции могут иметь или могут не иметь указатели кадра,
код завершения функции должен изменяться соответственно. Иногда код для
этих двух случаев полностью различен. Чтобы определить, требуется ли
указатель кадра, макрокоманда может обратиться к переменной
" frame_pointer_needed ". Значение переменной равно 1, если
компилируется функция, которая нуждается в указателе кадра.
Обычно, " FUNCTION_PROLOGUE " и " FUNCTION_EPILOGUE " должен обрабатывать
функции листа специальным образом. Переменная C " leaf_function "
отлична от нуля для такой функции. * См.: Функции Листа::.
На некоторых машинах некоторые функции восстанавливают из стека их
параметры на выходе, в то время как другие оставляют делать это
вызывающей функции. Например, 68020, когда задано " -mrtd ",
восстанавливает из стека параметры в функциях, которые имеют
фиксированное число параметров.
Ваше определение макрокоманды " RETURN_POPS_ARGS " решает, какие
функции восстанавливают из стека их собственные параметры.
" FUNCTION_EPILOGUE " требуется знать, что было решено. Переменная,
которая называется " current_function_pops_args " - число байтов
параметров, которые функция должна восстанавливать из стека.
* См.: Возвращение Скаляров::.
" DELAY_SLOTS_FOR_EPILOGUE "
Определите эту макрокоманду, если эпилог функции содержит слоты задержки,
в которые команды из остальной части функции могут "перемещаться".
Определение должно быть выражением C, чье значение - целое число,
представляющее число слотов задержки.
" ELIGIBLE_FOR_EPILOGUE_DELAY (INSN, N) "
Выражение C, которое возвращает 1, если INSN может быть помещен в слот
задержки номер N эпилога.
Параметр N - целое число, которое идентифицирует рассматриваемый слот
- 233 -
задержки (так как различные слоты могут иметь различные правила
"подходящести"). Это всегда неотрицателен и всегда меньше, чем
количество слотов задержки эпилога (которое возвращает
" DELAY_SLOTS_FOR_EPILOGUE "). Если Вы отклоняете данный insn для
данного слота задержки, в принципе, это может быть пересмотрено для
последующего слота задержки. Также, другой insns могут (по крайней мере,
в принципе) рассматриваться для пока незаполненного слота задержки.
Insns, принятые для заполнения слотов задержки эпилога, помещаются в
список RTL, сделанный объектами " insn_list ", сохраненными в переменной
" current_function_epilogue_delay_list ". Insn для первого слота задержки
входит в список первым. Ваше определение макрокоманды
" FUNCTION_EPILOGUE " должно заполнять слоты задержки при помощи вывода
insns в этом списке, обычно при помощи вызова " final_scan_insn ".
Вам не нужно определять эту макрокоманду, если Вы не определяли
" DELAY_SLOTS_FOR_EPILOGUE ".
6.6.10. Генерация Кода для Профилирования
Эти макрокоманды помогут Вам генерировать код для профилирования.
" FUNCTION_PROFILER (FILE, LABELNO) "
Оператор C или составной оператор для вывода в FILE некоторого
кода ассемблера, вызывающего подпрограмму профилирования " mcount ".
Перед вызовом код ассемблера должен загрузить адрес переменной-счетчика
в регистр, где " mcount " ожидает адрес. Имя этой переменной " LP "
сопровождается номером LABELNO, так что Вам следует генерировать имя,
используюя " LP%d " в " fprintf ".
Подробности того, как адрес должен передаваться " mcount ",
определяются вашей средой операционной системы, а не GNU CC. Чтобы
представить их себе, скомпилируйте маленькую программу для профилирования,
используя установленный C транслятор системы и посмотрите не получившийся
код ассемблера.
" PROFILE_BEFORE_PROLOGUE "
- 234 -
Определите эту макрокоманду, если код для профилирования функции должен
идти перед прологом функции. Обычно код профилирования идет после него.
" FUNCTION_BLOCK_PROFILER (FILE, LABELNO) "
Оператор C или составной оператор для вывода в FILE некоторого
кода ассемблера, инициализирующего профилирование базового блока для
текущего объектнго модуля. Этот код должен вызывать подпрограмму
" __ bb_init_func " по одному разу для каждого объектного модуля,
передавая ей к качестве единственного параметра адрес блока,
распределенного в объектном модуле.
Имя блока - локальный символ, созданный этим оператором:
ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0);
Конечно, так как Вы записываете определение
" ASM_GENERATE_INTERNAL_LABEL " так же, как для этой макрокоманды, Вы
можете сделать вырезку из определения этой макрокоманды и использовать
имя, которое, как Вы знаете, даст результат.
Первое слово этого блока - флаг, который будет отличный от нуля, если
объектный модуль уже был инициализирован. Поэтому проверяйте вначале это
слово и не вызывайте " __ bb_init_func ", если флаг отличен от нуля.
" BLOCK_PROFILER (FILE, BLOCKNO) "
Оператор C или составной оператор для увеличения счетчика, связанного с
базовым номером блока BLOCKNO. Базовые блоки нумеруются отдельно от нуля
внутри каждой трансляции. Счетчик, связанный с номером блока BLOCKNO -
это индекс BLOCKNO в векторе слов; имя этого массива - локальный символ, сделанный
создаваемый таким оператором:
ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 2);
Конечно, так как Вы записываете определение
" ASM_GENERATE_INTERNAL_LABEL " так же, как для этой макрокоманды, Вы
можете сделать вырезку из определения этой макрокоманды и использовать
имя, которое, как Вы знаете, даст результат.
- 235 -
" BLOCK_PROFILER_CODE "
Функция или функции C, которые необходимы в библиотеке для поддержки
блока профилирования.
6.7. Выполнение Varargs Макрокоманд
GNU CC имеет встроенную реализацию " varargs.h " и " stdarg.h ", которая
работает без изменения на машинах, которые передают параметры через стек.
Другие машины требуют собственных реализаций varargs, и
два машиннонезависимых файла заголовка должны иметь условные выражения,
чтобы включить их.
ANSI " stdarg.h " отличается от традиционного " varargs.h " главным образом
соглашением о вызовах для " va_start ". Традиционная реализация имеет только
один параметр, который является переменной для сохранения указателя на
параметр. ANSI реализация " va_start " имеет дополнительный второй параметр.
Предполагается, что пользователь пишет здесь последний именованный параметр
функции.
Однако, " va_start " не должна использовать этот параметр. Способ
нахождения окончания именованных параметров встроенными функциями описан ниже.
" __ builtin_saveregs () "
Используйте эту встроенную функцию, чтобы сохранить параметры из
регистров в памяти так, чтобы varargs механизм мог обращаться к ним. И
ANSI, и традиционная версии " va_start " должны использовать
" __ builtin_saveregs ", если Вы не используете вместо этого
" SETUP_INCOMING_VARARGS " (см. ниже).
На некоторых машинах " __ builtin_saveregs " открыта закодировано под
контролем над макрокомандой " EXPAND_BUILTIN_SAVEREGS ". На других
машинах она вызывает подпрограмму, написанную на языке ассемблеров,
находящуюся в " libgcc2.c ".
Код, сгенерированный для обращения к " __ builtin_saveregs " появляется в
начале функции, в противоположность тому, где обращение к
" __ builtin_saveregs " написано, независимо от того, что это за код.
- 236 -
Поэтому регистры должны быть сохранены перед тем, как функция начинает их
использовать для собственных целей.
" __ builtin_args_info (CATEGORY) "
Используйте эту встроенную функцию, чтобы найти первые безымянные
параметры в регистрах.
Вообще, машина может иметь несколько категорий регистров, используемых
для параметров, каждый для специфической категории типов данных.
(Например, на некоторых машинах регистры с плавающей запятой используются
для параметров с плавающей запятой, в то время как другие параметры
передаются в общих регистрах.) Чтобы добиться того, чтобы не-varargs
функции использовали соответствующее соглашение о вызовах, Вы должны
определить Тип данных " CUMULATIVE_ARGS " для записи того, сколько
регистров в каждой категории использовано.
" __ builtin_args_info " обращается к той же самой структуре данных типа
" CUMULATIVE_ARGS " после того, как обычное размещение параметра законченный
с ним, с CATEGORY, определяющей, к какому слову обращаться. Таким образом,
значение указывает первый неиспользуемый регистр в данной категории.
Обычно Вы использовали бы " __ builtin_args_info " в реализации
" va_start " для доступа к каждой категории только однажды и сохранения
значения в объекте ` va_list '. Поэтому " va_list " должна будет
обновлять значения, и нет никакого способа изменить значения,
обращаясь при помощи " __ builtin_args_info ".
" __ builtin_next_arg (LASTARG) "
Это - эквивалент " __ builtin_args_info ", для стековых параметров. Он
возвращает адрес первого безымянного стекового параметр, как тип
" void * ". Если " ARGS_GROW_DOWNWARD ", он возвращает адрес ячейки выше
первого безымянного стекового параметра. Используйте его в " va_start ",
чтобы инициализировать указатель для выборки параметров из стека. Также
используйте его в " va_start " , чтобы проверить, что второй параметр
LASTARG - последний именованный параметр текущей функции.
" __ builtin_classify_type (OBJECT) "
Так как каждая машина имеет собственные соглашения, какие типы данных
- 237 -
могут передаваться в каких видах регистров, ваша реализация " va_arg "
должна воплотить эти соглашения. Самый простой способ категоризировать
определенный тип данных заключается в использовании
" __ builtin_classify_type " вместе с " sizeof " и " __ alignof __ ".
" __ builtin_classify_type " игнорирует значение OBJECT, рассматривая
только тип данных. Он возвращает целое число, описывающее, какой это
тип - целое число, плавающее, указатель, структура и так далее.
Файл " typeclass.h " определяет перечисление, к которому Вы можете
использовать для интерпретации значений " __ builtin_classify_type ".
Эта макрокоманды машинного описания помогают выполнять varargs:
" EXPAND_BUILTIN_SAVEREGS (ARGS) "
Если определено, является выражением C, которое генерирует машинно-специфический
код для обращения к " __ builtin_saveregs ". Этот код будет помещен в
самое начало функции, перед тем, как будет получен доступ к какому-либо
параметру. Возвращаемое значение этой функции должно быть RTX, которое
содержит значение, которое следует использовать как результат
" __ builtin_saveregs ".
Параметр ARGS - " tree_list ", содержащий параметры, которые
были переданы " __ builtin_saveregs ".
Если эта макрокоманда не определена, транслятор выведет обычный
вызов библиотечной функции " __ builtin_saveregs ".
" SETUP_INCOMING_VARARGS (ARGS_SO_FAR, MODE, TYPE, PRETEND_ARGS_SIZE, SECOND_TIME)
Эта макрокоманда предлагает альтернативный вариант использования
" __ builtin_saveregs " и определения макрокоманды
" EXPAND_BUILTIN_SAVEREGS ". Используйте его, чтобы сохранить безымянные
регистровые параметры в стеке так, как будто все параметры были
переданы последовательно в стек. Как только это сделано, Вы можете
использовать стандартную реализацию varargs, которая работает на
машинах, которые передают все параметры через стек.
Параметр ARGS_SO_FAR - структура данных " CUMULATIVE_ARGS ",
- 238 -
содержащая значения, которые получаются после обработки именованных
параметров. Параметры MODE и TYPE описывают последний именованный
параметр - его машинный тип и тип данных как узел дерева.
Реализация макрокоманды должна делать две вещи: первое, поместить в
в стек все регистровые параметры, *не* используемые для именованных
параметров, и второе, сохранить размер этих записанных в стек данных,
в переменной типа " int ", чье имя поддерживается как параметр
PRETEND_ARGS_SIZE. Значение, которое Вы сохраняете, будет служить как
дополнительное смещение для установки кадра стека.
Поскольку Вы должны генерировать код, чтобы поместить в стек безымянные
параметры во время компиляции, не зная их типов данных,
" SETUP_INCOMING_VARARGS " имеет смысл использовать только на машинах,
которые имеют только одну категорию регистровых параметров и используют
ее одинаково для всех типов данных.
Если параметр SECOND_TIME отличен от нуля, это означает, что параметры
функции анализируются второй раз. Так случается для встроенной функции,
которая фактически не компилируется до конца исходного файла.
Макрокоманда " SETUP_INCOMING_VARARGS " не должна генерировать никаких
команд для этого случая.
" STRICT_ARGUMENT_NAMING "
Определите эту макрокоманду, если место, где передается параметр
функции, зависит от того, является ли он именованным параметром.
Эта макрокоманда управляет тем, как устанавливается параметр NAMED
к " FUNCTION_ARG " для функций varargs и stdarg. Если эта макрокоманда
определена, параметр NAMED - всегда истина для именованных параметров и
ложь - для безымянных параметров. Если она не определена, но
" SETUP_INCOMING_VARARGS " определена, то все параметры обрабатываются
как именованные. В противном случае, все именованные параметры, за
исключением последнего, обрабатываются как именованные.
6.8. Trampolines для вложенных функций
- 239 -
"Trampoline" - маленькая часть кода, которая создается во время выполнения,
когда принимается адрес вложенной функции. Она обычно располагается в стеке,
во кадре стека содержащей функции. Эти макрокоманды сообщают GNU CC, как
генерировать код, чтобы распределять и инициализировать trampoline.
Команды в trampoline должны делать две вещи: загрузка
константного адреса в статический цепной регистр, и переход к реальному
адресу вложенной функции. На CISC машинах типа m68k это требует двух команд,
перемещение и переход. Тогда два адреса присутствуют в trampoline как
непосредственные операнды размера word. На RISC машинах часто необходимо
загрузить каждый адрес в регистр в двух частях. Тогда части каждого адреса
образуют отдельные непосредственные операнды.
Код, сгенерированный, чтобы инициализировать trampoline, должен сохранить
переменные части - статическое цепное значение и адрес функции - в
непосредственные операнды команд. На CISC машине это просто вопрос копирования
каждого адреса к ссылку памяти в соответствующее смещение от начала trampoline.
На RISC машине может оказаться необходимо выбирать части адреса и сохранять
их отдельно.
" TRAMPOLINE_TEMPLATE (FILE) "
Оператор C для вывода в поток FILE кода ассемблера для
блока данных, который содержит постоянные части trampoline.
Этот код не должен включать метку - о метке позаботятся автоматически.
" TRAMPOLINE_SECTION "
Имя подпрограммы для включения в раздел, в который
должен быть помещен trampoline шаблон (* См.: Разделы::.).
Значение по умолчанию - значение " readonly_data_section ", который
помещает trampoline в разделе, содержащем данные только для чтения.
" TRAMPOLINE_SIZE "
Выражение C для размера в байтах trampoline, как целое число.
" TRAMPOLINE_ALIGNMENT "
Выравнивание, требуемое для trampolines, в битах.
Если Вы не определяете эту макрокоманду, для выравнивания trampolines
- 240 -
используется значение " BIGGEST_ALIGNMENT ".
" INITIALIZE_TRAMPOLINE (ADDR, FNADDR, STATIC_CHAIN) "
Оператор C для инициализации переменных частей trampoline.
ADDR - RTX для адреса trampoline; FNADDR - RTX
для адреса вложенной функции; STATIC_CHAIN - RTX для
статического цепного значения, которое должно быть передано функции,
когда она вызывается.
" ALLOCATE_TRAMPOLINE (FP) "
Выражение C для распределения места во время выполнения для trampoline.
Значение выражения должно быть RTX, представляющим ссылку памяти
на место для trampoline.
Если эта макрокоманда не определена, по умолчанию trampoline
распределяется как слот стека. Это значение по умолчанию подходит для
большинства машин. Исключения - машины, где невозможно выполнение команд
в области стека. На таких машинах Вам, возможно, следует создать
отдельный стек, используя эту макрокоманду в сочетании с
" FUNCTION_PROLOGUE " и " FUNCTION_EPILOGUE ".
FP указывает на структуру данных, " struct function", которая
описывает состояние трансляции непосредственных содержащий
функции для функции, которой принадлежит trampoline. Обычно
(когда " ALLOCATE_TRAMPOLINE " не определено), слот стека для
trampoline находится во кадре стека этой содержащей функции.
Другие cтратегии распределения, вероятно, должны делать что-либо
аналогичное с этой информацией.
Выполнение trampolines трудно на многих машинах, потому что они имеют
раздельные кэши команд и данных. Запись в ячейку стека не может очистить
память в кэше команд, так что когда
программа переходит к этой ячейке, выполняется старое содержимое.
Имеется два возможных решения. Одно заключается в очистить относящихся к
делу частей кэша команд всякий раз, когда trampoline устанавливается. Другой
- сделать все trampoline идентичными, сделав в них переходы на стандартную
подпрограмму. Вышеупомянутая методика делает выполнение trampoline быстрее;
- 241 -
последний способ делает инициализацию быстрее.
Чтобы очистить кэш команд, когда trampoline инициализирован,
определите следующие макрокоманды, которые описывают форму кэша.
" INSN_CACHE_SIZE "
Полный размер в байтах кэша.
" INSN_CACHE_LINE_WIDTH "
Длина в байтах каждой линии кэша. Кэш разделен на линии кэша, которые
являются непересекающимися слотами, каждый из которых содежит часть
данных, выбранных из памяти. Каждый раз, когда данные записываются в
кэш, вся линия читается целиком. Данные, загруженные в линию кэша,
всегда выравниваются на границе, равной размеру линии.
" INSN_CACHE_DEPTH "
Число альтернативных линий кэша, которые могут содержать любую конкретную
ячейку памяти.
Альтернативно, если машина имеет системные вызовы или команды для очистки
кэша команды непосредственно, Вы можете определять следующую макрокомандуа.
" CLEAR_INSN_CACHE (BEG, END) "
Если определена, расширяется до выражения C, очищающего *кэш команд* в
указанном интервале. Если она не определена, и макрокоманда
INSN_CACHE_SIZE определена, генерируется некоторый родовой код для
очистки кэша. Определение этой макрокоманды обычно является цепочкой
операторов " asm ". BEG и END - выражения-указатели.
Чтобы использовать стандартную подпрограмму, определите следующую
макрокоманду. Кроме того, Вы должны удостовериться, что команды в trampoline
заполняют всю линию кэша идентичными командами, или же гарантируют, что
начало trampoline кода всегда выравнивается по той же самой точке на линии
кэша. Смотрите " m68k.h " как руководство.
" TRANSFER_FROM_TRAMPOLINE "
Определите эту макрокоманду, если trampolines нуждаются в специальной
подпрограмме для выполнения их работы. Макрокоманда должна расширяться до
- 242 -
ряда " asm " операторов, которые будут компилироваться GNU CC. Они входят
в библиотечную функцию под названием " __ transfer_from_trampoline ".
Если Вам нужно избежать выполнения кода обычного пролога
cкомпилированной функции C, когда Вы переходите к подпрограмме, Вы можете
добиться этого, помещая Вашу собственную специальную метку в коде
ассемблера. Использование один оператор " asm ", чтобы сгенерировать
метку ассемблера, и другой, чтобы сделать метку глобальной. Тогда
trampolines смогут использовать эту метку для перехода
непосредственно к вашему специальному коду ассемблера.
6.9. Неявные Обращения к Библиотечным Подпрограммам
"MULSI3_LIBCALL"
Строковая константа C имени функции умножения одного знакового полного слова на
другое. Если вы не определяете эту макрокоманду, используется имя, заданное по
умолчанию, "__mulsi3" функции, определенной в "libgcc.a".
"DIVSI3_LIBCALL"
Строковая константа C имени функции деления одного знакового полного слова на
другое. Если вы не определяете эту макрокоманду, используется имя, заданное по
умолчанию, "__divsi3" функции, определенной в "libgcc.a".
"UDIVSI3_LIBCALL"
Строковая константа C имени функции деления одного беззнакового полного слова
на другое. Если вы не определяете эту макрокоманду, используется имя, заданное
по умолчанию " __udivsi3 " функции, определенной в "libgcc.a".
"MODSI3_LIBCALL"
Строковая константа C имени функции вычисления остатка деления одного знакового
полного слова на другое. Если вы не определяете эту макрокоманду, используется
имя, заданное по умолчанию " __modsi3 " функции, определенной в "libgcc.a".
"UMODSI3_LIBCALL"
Строковая константа C имени функции вычисления остатка деления одного
беззнакового полного слова на другое. Если вы не определяете эту макрокоманду,
используется имя, заданное по умолчанию "__umodsi3" функции, определенной в
- 243 -
"libgcc.a"
"MULDI3_LIBCALL"
Строковая константа C имени функции умножения одного знакового двойного слова
на другое. Если вы не определяете эту макрокоманду, используется имя, заданное
по умолчанию "__muldi3" функции, определенной в "libgcc.a".
"DIVDI3_LIBCALL"
Строковая константа C имени функции деления одного знакового двойного слова на
другое. Если вы не определяете эту макрокоманду, используется имя, заданное по
умолчанию "__divdi3" функции, определенной в "libgcc.a".
"UDIVDI3_LIBCALL"
Строковая константа C имени функции деления одного беззнакового полного слова
на другое. Если вы не определяете эту макрокоманду, используется имя, заданное
по умолчанию "__ divdi3" функции, определенной в "libgcc.a".
"MODDI3_LIBCALL"
Строковая константа C имени функции вычисления остатка деления одного знакового
двойного слова на другое. Если вы не определяете эту макрокоманду, используется
имя, заданное по умолчанию "__moddi3" функции, определенной в "libgcc.a".
"UMODDI3_LIBCALL"
Строковая константа C имени функции вычисления остатка деления одного
беззнакового полного слова на другое. Если вы не определяете эту макрокоманду,
используется имя, заданное по умолчанию "__umoddi3" функции, определенной в
"libgcc.a".
"INIT_TARGET_OPTABS"
Определите эту макрокоманду как оператор C, который объявляет дополнительные
библиотечные подпрограммы и переименовывает существующие. "Init_optabs"
вызывает эту макрокоманду после инициализации всех нормальных библиотечных
подпрограмм.
"TARGET_EDOM"
Значение "EDOM" на целевой машине в виде целочисленной константы C. Если вы не
определяете эту макрокоманду, GNU CC не пытается вносить значение "EDOM" в
"errno" непосредственно. Посмотрите в "/usr/include/errno.h" чтобы найти
- 244 -
значение "EDOM" для вашей системы.
Если вы не определяете "TARGET_EDOM", то откомпилированный код сообщит об
ошибке, заставляя это делать библиотечную функцию. Если математические функции
на вашей системе при ошибке используют "matherr", то вы должны оставить
"TARGET_EDOM" неопределенным, чтобы "matherr" могла нормально использоваться.
"GEN_ERRNO_RTX"
Определите эту макрокоманду как выражение C для создания выражения rtl, которое
ссылается на глобальную "переменную" "errno". (На некоторых системах, "errno"
может не быть переменной.) Если вы не определяете эту макрокоманду,
используется приемлемое значение по умолчанию.
"TARGET_MEM_FUNCTIONS"
Определите эту макрокоманду, если GNU CC должен генерировать обращения к
библиотечным функциям System V (и ANSI C) "memcpy" и "memset", а не к
BSD-функциям "bcopy" и "bzero".
"LIBGCC_NEEDS_DOUBLE"
Определите эту макрокоманду только если параметры типа "float" не могут быть
переданы библиотечным подпрограммам (так что они должны быть преобразованы в
"double"). Эта макрокоманда воздействует и на то, как генерируются библиотечные
вызовы и как библиотечные подпрограммы в "libgcc1.c" принимают свои параметры.
Это полезно на машинах, где параметры с плавающей и фиксированной точкой
передаются по-разному, например в i860.
"FLOAT_ARG_TYPE"
Определите эту макрокоманду, чтобы изменить тип, используемый библиотечными
подпрограммами, чтобы передавать параметры типа "float". (По умолчанию, они
используют объединение "float" и "int".)
Очевидно выбор должен быть "float", но это не будет работать на традиционных
трансляторах C, которые все параметры, объявленные как "float" переделывают в
"double". Чтобы избежать такого преобразования, библиотечные подпрограммы
запрашивают значение какого-то другого типа, а затем обрабатывают его как
"float".
На некоторых системах никакой другой тип не будет так работать. На этих
- 245 -
системах вы должны вместо этой использовать "LIBGCC_NEEDS_DOUBLE", заставляя
транслятор преобразовывать "double"-значения до того, как они переданы.
"FLOATIFY (PASSED-VALUE)"
Определите эту макрокоманду, чтобы изменить способ, которым библиотечные
подпрограммы повторно назначают параметр типа "float" как "float" вместо типа,
в качестве которого он был передан. По умолчанию это выражение, которое берет
"float"-поле из объединения.
"FLOAT_VALUE_TYPE"
Определите эту макрокоманду, чтобы изменить тип, используемый для возвращения
значения библиотечными подпрограммами, которые должны возвращать тип "float".
(По умолчанию они используют "int".)
Очевидный выбор был бы "float", но это не будет работать на трансляторе
традиционного C, который сам преобразовывает значения, объявленные как "float",
в "double".
"INTIFY (FLOAT-VALUE)"
Определите эту макрокоманду, чтобы изменить способ, каким образом значение,
возвращаемое библиотечной подпрограммой, которая должна возвращать тип "float",
должно быть упаковано, чтобы возвратить его. Эти функции объявляются как
возвращающие тип "FLOAT_VALUE_TYPE" (обычно "int").
Эти значения не могут быть возвращены как тип "float" потому что традиционные C
трансляторы сами преобразовывают значение в "double".
Локальная переменная, названная "intify" всегда доступна, когда используется
макрокоманда "INTIFY". Она - объединение "float"-поля с именем "f" и поля с
именем "i" типа "FLOAT_VALUE_TYPE" или "int".
Если вы не определяете эту макрокоманду, работает заданное по умолчанию
определение : копирует значения через это объединение.
"Nongcc_SI_type"
Определите эту макрокоманду как имя типа данных, соответствующего "SImode" в
собственном C-трансляторе системы.
- 246 -
Вы не должны определять эту макрокоманду, если вышеуказанный тип - "long int",
а это обычно он и есть.
"Nongcc_word_type"
Определяйте эту макрокоманду как имя типа данных, соответствующего word_mode в
собственном C-трансляторе системы.
Вы не должны определять эту макрокоманду, если вышеуказанный тип - "long int",
а это обычно так.

CC-BY-CA Анатольев А.Г., 31.01.2012