Клуб любителей LinuxLinux users group of Taraz Компилятор языков C, C++, Objective C gcc 2.7 (часть 2) Ричард Столлман (Richard Stallman) Перенос на новые архитектуры ______________________________________________________________________ Table of Contents 1. GNU CC и переносимость 2. Интерфейс вывода в GNU CC 3. Проходы и Файлы Транслятора 4. Представление RTL 4.1 Типы объектов RTL 4.2 Доступ к Операндам 4.3 Флаги в выражении RTL 4.4 Машинные типы 4.5 Типы константных выражений 4.6 Регистры и память 4.7 Операции сравнения 4.8 Битовые поля. 4.9 Преобразования 4.10 Объявления 4.11 Выражения с побочным эффектом 4.12 Вложенные побочные эффекты на адресах 4.13 Команды Ассемблера как выражения 4.14 Insns 4.15 RTL Представление Insns - Вызовов Функций 4.16 Предположения о Совместном Использовании Структур 4.17 Чтение RTL 5. Машинные Описания 5.1 Все про Образцы Команд 5.2 Пример " define_insn " 5.3 RTL Шаблоны 5.4 Шаблоны вывода и замена операндов 5.5 Операторы С для вывода ассемблера 5.6 Ограничения операндов 5.6.1 Простые Ограничения 5.6.2 Многократные Альтернативные Ограничения 5.6.3 Предпочтения классов регистров 5.6.4 Символы модификатора ограничения 5.6.5 Ограничения для Специфических Машин 5.6.6 Без ограничений 5.7 Стандартные Образцы Имен Для Генерации 5.8 Когда Порядок Образцов Играет Роль 5.9 Взаимозависимость Образцов 5.10 Определение Образцов Команд Перехода 5.11 Канонизация Команд 5.12 Машинно - специфические локальные оптимизации 5.13 Определенные RTL последовательности для генерации кода 5.14 Как разделять инструкции 5.15 Атрибуты команд 5.15.1 Определение атрибутов и их значений. 5.15.2 Атрибут-выражения 5.15.3 Установка значения атрибута для insns. 5.15.4 Примеры спецификаций атрибута 5.15.5 Вычисление Длины Insn 5.15.6 Постоянные атрибуты 5.15.7 Планирование слота задержки 5.15.8 Определения функциональных модулей 6. Target Description Macros 6.1 Управление Драйвером Трансляции " gcc " 6.2 Целевая Спецификация времени выполнения 6.3 Распределение Памяти 6.4 Формат Исходных Типов Данных Языка 6.5 Использование Регистров 6.5.1 Базовые Характеристики Регистров 6.5.2 Порядок Распределения Регистров 6.5.3 Как Располагать Значения В Регистрах 6.5.3.1 Обработка leaf-функций 6.5.4 Регистры, образующие стек 6.5.5 Устаревшие Макрокоманды для Управления Использованием Регистров 6.6 Классы Регистров 6.7 Формат Стека и Соглашения о вызовах 6.7.1 6.6.1.Основные Параметры Стека 6.7.2 Регистры, Адресующие Кадр стека 6.7.3 Удаление Указателя Кадра и Указателя Аргументов 6.7.4 Передача Аргументов Функции через Стек 6.7.5 Передача Параметров В Регистрах 6.7.6 Как Возвращаются Значения Скалярной Функции 6.7.7 Как Возвращаются Структурированные Значения 6.7.8 Распределение Регистров с Сохранением 6.7.9 Вход и Выход из Функции 6.7.10 Генерация Кода для Профилирования 6.8 Выполнение Varargs Макрокоманд 6.9 Trampolines для вложенных функций 6.10 Неявные Обращения к Библиотечным Подпрограммам 6.11 Способы адресации 6.12 Состояние Кода Условия 6.13 Описание Относительных Стоимостей Операций 6.14 Деление Вывода на Разделы 6.15 Позиционно-Независимый Код 6.16 Определение Выходного Языка Ассемблера 6.16.1 Полная Структура Ассемблерного Файла 6.16.2 Вывод Данных 6.16.3 Вывод Неинициализированных Переменных 6.16.4 Вывод и Генерация Меток 6.16.5 Как обрабатываются функции инициализации 6.16.6 Макрокоманды, управляющие программами инициализации 6.16.7 Вывод Команд Ассемблера 6.16.8 Вывод Таблиц Распределения 6.16.9 Команды Ассемблера для Выравнивания 6.17 Управление Информационным Форматом отладки 6.17.1 Макрокоманды, которые воздействуют на все отладочные форматы 6.17.2 Специфические Опции для Вывода DBX 6.17.3 Открытые Ловушки для Формата DBX 6.17.4 Имена Файлов в Формате DBX 6.17.5 Макрокоманды для SDB и Вывода DWARF 6.18 Кросс-трансляция и числа с плавающей точкой 6.19 Смешанные параметры 7. Файл конфигурации 8. Фрагменты Makefile-а 8.1 Фрагменты Makefile, отвечающие целевой машине 8.2 Фрагменты Makefile, отвечающие главной машине ______________________________________________________________________ 1. GNU CC и переносимость Основная цель GNU CC состояла в создании хорошего, быстрого транслятора для класса машин, на которых предполагается использование системы GNU: 32-битные машины, адресующие 8-битные байты и имеющие несколько регистров общего назначения. Элегантность, теоретическая мощность и простота стояли только на втором месте. GNU CC получает большую часть информации относительно целевой машины из описания машины, которое дает алгебраическую формулу для каждой машинной команды. Это - очень чистый способ описать целевую машину. Но когда транслятор нуждается в информации, которую трудно выразить таким способом, я не колебался определять для данного случая параметр машинного описания. Цель переносимости - уменьшить общую работу, необходимую на трансляторе; само по себе это не представляет интереса. GNU CC не содержит машинно-зависимого кода, но он содержит код, который зависит от машинных параметров типа endianness (имеет старший байт больший или меньший адрес в слове) и доступности адресации с автоматическим инкрементом. В проход генерации RTL часто необходимо иметь набор cтратегий для генерации кода для специфического вида дерева синтаксиса, cтратегий, пригодных для использования при различных комбинациях параметров. Часто я не пытался охватить все возможные случаи, а рассматривал только общие или только те, которые я встречал. В результате новая целевая машина может потребовать дополнительных cтратегий. Если такое случится, Вы узнаете об этом, потому что транслятор вызовет " abort ". К счастью, новые cтратегии могут быть добавлены машинно-независимым способом, и будет действовать только на тех целевых машинах, на которых они нужны. 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 байтов. 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, может вызывать некоторые библиотечные подпрограммы. Большинство из них обрабатывают арифметику, для которой не имеется никаких команд. Это включает умножение и деление на некоторых машинах, и операции с плавающей точкой на любой машине, на которой поддержка плавающей точки отключена с помощью "-msoft-float". Некоторые стандартные части библиотеки C, типа "bcopy" или "memcpy", также вызываются автоматически. Обычный интерфейс вызова функций используется для вызова библиотечных подпрограмм. Эти библиотечные подпрограммы должны быть определены в библиотеке "libgcc.a", которую GNU CC автоматически ищет всякий раз, когда линкует программу. На машинах, которые имеют команды умножения и деления при использовании аппаратной поддержки плавающей точки "libgcc.a" обычно не необходим, но ищется на всякий случай. Каждая арифметическая функция определена в " libgcc1.c " для использования соответствующего арифметического оператора C. Как только файл будет скомпилирован другим транслятором C, который поддерживает все арифметические операторы C, этот файл будет работать машиннонезависимо. Однако, " libgcc1.c " не будет работать, если его скомпилировать GNU CC, потому что каждая арифметическая функция скомпилируется в обращение к себе! 3. Проходы и Файлы Транслятора Полная структура управления транслятора находится в "toplev.c". Это файл ответственен за инициализацию, декодирование параметров, открытие и закрытие файлов и последовательность проходов. Проход синтаксического анализа вызывается только однажды для анализа всего ввода. Промежуточный код RTL для функции генерируется, когда функция анализируется. Обработка происходит пооператорно. Каждый оператор считывается как синтаксическое дерево и затем преобразуется в RTL; после этого память, содержащая дерево оператора, освобождается. Типы (и выражения для их размеров), объявления, представления связывания контуров и как они вкладываются сохраняются, пока функция не скомпилируется; все они необходимы, чтобы вывести информацию об отладке. Каждый раз, когда при проходе синтаксического анализа читается полное определение функции или объявление верхнего уровня, вызывается функция " rest_of_compilation " или функция " rest_of_decl_compilation " в " toplev.c ", которые ответственны за всю дальнейшую необходимую обработку, заканчивающуюся выводом на языке ассемблера. Остальные проходы транслятора вызываются последовательно внутри " rest_of_compilation ". Когда эта функция возвращается из компиляции определения функции, память, используемая для трансляции этого определения, полностью освобождается, если это не встроенная функция (См.: Встроенная функция работает так же быстро, как Макрокоманда: Встраивание.). Вот список всех проходов транслятора и их исходных файлов. Также включено описание того, как запросить отладочные дампы при помощи опции " -d ". ╥ Синтаксический анализ. Этот проход читает весь текст определения функции и строит частичные синтаксические деревья. Это и генерация RTL не являются больше отдельными проходами (как раньше), но проще думать о них как об отдельных. Представление дерева не следует полностью синтаксису 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. Фактически она выполняется пооператорно во время синтаксического анализа, но для большинства целей о ней можно думать как об отдельном проходе. Именно здесь находится большая часть кода, зависящего от машины, так для 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, когда функция вызывается. Файл заголовка " integrate.h " также используется для этой цели. Опция " -dr " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .rtl " к имени входного файла. ╥ Оптимизация переходов. Этот проход упрощает переходы к следующей команде, переходы через переходы и переходы к переходам. Он удаляет неиспользуемые метки и недостижимый код, за исключением того, что недостижимый код, который содержит цикл, не распознается как недостижимый в этот проход. (Такие циклы удаляются позже при анализе базовых блоков). Он также преобразует некоторый код, первоначально написанный с переходами, в последовательности команд, которые непосредственно устанавливают значения по результатам сравнений, если машина имеет такие команды. Оптимизация переходов выполняется два или три раза. Первый раз происходит сразу после генерации RTL. Второй раз - после CSE, но только если CSE сообщает, что повторная оптимизация перехода необходима. Последний раз - перед заключительным проходом. На этом проходе перекрестные переходы и стирание пустых команд перемещения выполняются вместе с оптимизациями, описанными выше. Исходный файл этого прохода - " jump.c ". Опция " -dj " вызывает дамп отладки RTL кода после первого выполнения этого прохода. Имя файла дампа получается добавлением " .jump " к имени входного файла. ╥ Просмотр регистров. Этот проход находит, когда первый и когда последний раз использовался каждый регистр; это потребуется для удаления общих подвыражений. Источник находится в " regclass.c ". ╥ Слияние переходов. Этот проход распознает условные переходы, ветви которых ведут к идентичному или обратному тесту. Такие переходы могут быть "слиты" через второй условный тест. Исходный текст этого прохода находится в " jump.c ". Эта оптимизация выполняется, только если установлена опция "-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 ". ╥ Потоковый анализ данных (" flow.c "). Этот проход делит программу на базовые блоки (и по ходу работы удаляет недостижимые циклы); затем он вычисляет, какие псевдорегистры "живут" в каждой точке программы и делает первую команду, которая использует значение, указывающей на команду, которая вычислила значение. Этот проход также удаляет вычисления, результаты которых никогда не используются, и объединяет ссылки на память с командами сложения или вычитания для получения адресации с автоинкрементом и автодекрементом. Опция " -df " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .flow " к имени входного файла. Если используется "глупое" распределение регистров, этот файл дампа отражает полные результаты такого распределения. ╥ Комбинирование команд (" combine.c "). Этот проход делает попытку объединить группы из двух или трех команд, относящихся к потоку данных, в одиночные команды. Он объединяет RTL выражения, заменяя их на команды, упрощает результат, использующий алгебру, и затем пытается согласовать результат с машинным описанием. Опция " -dc " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .combine " к имени входного файла. ╥ Планирование команд (" sched.c "). Этот проход ищет команды, чей вывод не будет доступен ко времени его использования в последующих командах. (Команды загрузки памяти и работы с плавающей точкой часто ведут себя так на RISC машинах). Он переупорядочивает команды внутри базового блока, чтобы попытаться разделить определение и использование элементов, которые иначе вызвали бы приостановку работы. Планирование команд выполняется дважды. Первый раз - сразу после комбинирования команд и второй - сразу после перезагрузки. Опция " -dS " вызывает дамп отладки RTL кода после первого выполнения этого прохода. Имя файла дампа получается добавлением " .sched " к имени входного файла. ╥ Выбор класса регистров. RTL код просматривается, чтобы выяснить, какой класс регистров является самым лучшим для каждого псевдорегистра. Исходный файл - " regclass.c ". ╥ Локальное распределение регистров (" local-alloc.c "). Этот проход распределяет аппаратные регистры псевдорегистрам, которые используются только внутри одного базового блока. Поскольку базовый блок линеен, он может использовать быстрые и мощные методы и получать очень хорошие результаты. Опция " -dl " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .lreg " к имени входного файла. ╥ Глобальное распределение регистров (" global.c "). Этот проход распределяет аппаратные регистры для оставшихся псевдорегистров (тех, чьи промежутки жизни не содержатся в одном базовом блоке). ╥ Перезагрузка. Этот проход перенумеровывает псевдорегистры номерами аппаратных регистров, в которые они были распределены. Псевдорегистры, не получившие аппаратных регистров заменяются на слоты стека. Затем он находит команды, которые являются недопустимыми, потому что значение не смогло закончиться в регистре или закончилось в регистре неправильного вида. Он исправляет эти команды, временно перезагружая эти значения в регистры. Генерируются дополнительные команды для копирования. Проход перезагрузки также может устранять указатель кадра и вставлять команды для сохранения и восстановления регистров, использующихся вызываемой функцией, до и после вызова. Исходные файлы - " reload.c " и " reload1.c ", а также заголовок " reload.h ", используемый для связи между ними. Опция " -dg " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .greg " к имени входного файла. ╥ Планирование команд повторяется здесь, чтобы попытаться избежать остановки потока команд из-за загрузок памяти, сгенерированных для "пролитых" псевдорегистров. Опция " -dR " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .sched2 " к имени входного файла. ╥ Оптимизация переходов повторяется, включая на этот раз перекрестные переходы и удаление пустых команд перемещения. Опция " -dJ " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .jump2 " к имени входного файла. ╥ Планирование задержанных переходов. Этот необязательный проход пытается найти команды, которые могут войти в слоты задержки другой команд, обычно переходов и вызовов функций. Имя исходного файла - " reorg.c ". Опция " -dd " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .dbr " к имени входного файла. ╥ Преобразование из использования некоторых аппаратных регистров к использованию регистров стека может быть выполнено в этом месте. В настоящее время это поддерживается только для регистров сопроцессора Intel 80387 с плавающей точкой. Имя исходного файла - " reg-stack.c ". Опция " -dk " вызывает дамп отладки RTL кода после этого прохода. Имя файла дампа получается добавлением " .stack " к имени входного файла. ╥ Заключительный проход. Этот проход выводит код ассемблера для функции. Он является также ответственным за идентификацию ложных команд проверки и сравнения. Специфическая для машины оптимизация выполняется в это же время. Последовательности для входа и выхода из функции генерируются непосредственно как код ассемблера в этом проходе; они никогда не существуют как 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 ". ╥ Несколько проходов используют распознаватель команд, который состоит из " 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 ". 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, которые создают машинные описания. Вектор содержит произвольное число указателей на выражения. Число элементов в векторе явно указано в векторе. Форма записи вектора состоит из квадратных скобок (" [...] "), в которые заключаются элементы, записанные последовательно с пропусками, разделяющими их. Векторы длины ноль не создаются; вместо них используются пустые указатели. Выражения классифицируются по "кодам выражения" (также называемым 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. Доступ к Операндам Для каждого типа выражения " rtl.def " указывает количество содержащихся объектов и их виды следующим способом: " e " для выражения (фактически указателя на выражение), " i " для целого числа, " w " для широкого целого числа, " s " для строки и " E " для вектора выражений. Последовательность символов для кода выражения называется его форматом. Таким образом, форматом " subreg " является " ei ". Иногда используются несколько других символов управления форматом: " u " эквивалентно " e " за исключением того, что оно печатается по-другому в отладочных дампах. Это используется для указателей на insns. " n " эквивалентно " i " за исключением того, что оно печатается по-другому в отладочных дампах. Это используется для номера строки или количества кода insn " note ". " S " обозначает строку, которая является необязательной. В объектах RTL в ядре " S " эквивалентно " s ", но когда объект читается из " md " файла, строковое значение этого операнда может быть опущено. Опущенная строка считается пустой. " V " обозначает вектор, который является необязательным. В объектах RTL в ядре " V " эквивалентно " E ", но когда объект читается из " md " файла, векторное значение этого операнда может быть опущено. Опущенный вектор - то же, что и вектор, не имеющий элементов. " 0 " означает слот, чей содержание не попадает ни в какую нормальную категорию. " 0 " слоты не печатаются вообще в дампах, и часто используются специальными способами в маленьких частях транслятора. Имеются макрокоманды для получения числа операндов, формата и класса кода выражения: Число операндов RTX кода CODE. Формат RTX кода CODE как строка C. Одиночный символ, представляющий тип операции RTX, которую выполняет код CODE. Определены следующие классы: RTX код, который представляет фактический объект, типа " reg " или " mem ". " subreg " не принадлежит к этому классу. RTX код для сравнения. Коды в этом классе " NE ", " EQ ", " LE ", " LT ", " GE ", " GT ", " LEU ", " LTU ", " GEU ", " GTU ". RTX код для унарной арифметической операции, типа " neg ". RTX код для коммутативной бинарной операции, отличной от " NE " и " EQ " (которые имеют класс " < "). RTX код для некоммутативной двоичной операции, типа " MINUS ". RTX код для операции с битовым полем, " ZERO_EXTRACT " или " SIGN_EXTRACT ". RTX код для трех других входных операций, типа " IF_THEN_ELSE ". RTX код для машинного insn (" INSN ", " JUMP_INSN " и " CALL_INSN "). RTX код для того,что соответствует insns, типа " MATCH_DUP ". Все другие RTX коды. К операндам выражений обращаются, используя макрокоманды " XEXP ", " XINT ", " XWINT " и " XSTR ". Каждая из этих макрокоманд имеет два параметра: указатель на выражение (RTX) и номер операнда (отсчитываемый от нуля). Таким образом, XEXP (X, 2) обращается к операнду 2 выражения X как к выражению. XINT (X, 2) обращается к тому же самому операнду как к целому числу. Макрокоманда" XSTR ", использованная тем же самым способом, получила бы этот операнд как строку. К любому операнду можно обращаться как к целому числу, выражению или строке. Вы должны выбрать правильный метод доступа для вида значения, фактически содержащегося в операнде. Это делается при помощи кода выражения, содержащего операнд. Таким же образом Вы можете узнать, сколько это выражение имеет операндов. Например, если X - выражение " subreg ", Вы знаете, что оно имеет два операнда, к которым можно корректно обращаться как " XEXP (X, 0) " и " XINT (X,1) ". Если Вы напишете " XINT (X, 0) ", Вы получите адрес операнд выражения как целое число; это может быть иногда полезно, но чтобы получить такой результат, лучше писать " (int) XEXP (X, 0) ". " XEXP( X, 1) " тоже скомпилируется без ошибки и возвратит второй, целочисленный операнд,приведенный к указателю на выражение, обращение к которому, скорее всего, приведет к аварийному отказу. Ничто не запрещает Вам писать " XEXP( X, 28) ", но эта запись обратится к памяти после конца выражения с непредсказуемыми результатами. Доступ к операндам, которые являются векторами, сложнее. Вы можете использовать макрокоманду " XVEC ", чтобы получить указатель на вектор непосредственно, или макрокоманды " XVECEXP " и " XVECLEN ", чтобы обратиться к элементам и длине вектора. Получает указатель на вектор, который является операндом номер IDX в выражении EXP. Получает длину (число элементов) в векторе, который является операндом номер IDX в выражении EXP. Это значение типа " int ". Обращается к элементу номер ELTNUM в векторе, который является операндом номер IDX в выражении EXP. Это значение RTX. При вызове этой макрокоманды нужно быть уверенным, что ELTNUM не меньше нуля и меньше чем " XVECLEN (EXP, IDX) ". Все макрокоманды, определенные в этом разделе, расширяются в именующие выражения и, следовательно, могут использоваться для присваивания значения операндам, длинам и элементам векторов так же, как и для обращения к ним. 4.3. Флаги в выражении RTL Выражения RTL содержат несколько флагов (битовых полей с одним битом), которые используются в некоторых типах выражений. Наиболее часто к ним обращаются следующими макрокомандами: В выражениях " mem " отлично от нуля для переменных ссылок памяти. Сохраняется в поле " volatil " и печатается как " /v ". В выражениях " mem " отлично от нуля для ссылки на всю структуру, объединение или массив, или на их компонент. Ноль для ссылки на скалярную переменную или через указатель на скаляр. Сохраняется в поле " in_struct " и печатается как " /s ". В выражениях " reg " отлично от нуля, если этот регистр "живет" только в коде проверки на выход некоторого цикла. Сохраняется в поле " in_struct " и печатается как " /s ". В " reg " отлично от нуля, если соответствует переменной, имеющейся в исходном тексте пользователя. Ноль для временных переменных, внутренне сгенерированных транслятором. Сохраняется в поле " volatil " и печатается как " /v ". Отлично от нуля в " reg ", если это - место, в которое должно быть возвращено значение функции. (Это возможно только для аппаратного регистра.) Сохраняется в поле " integrated " и печатается как " /i ". Тот же самый аппаратный регистр может использоваться также для значений функций, вызываемых этой, но " REG_FUNCTION_VALUE_P " - ноль для этого вида использования. Отлично от нуля в " subreg ", если делается во время доступа к объекту, продвинутому на более широкий тип в соответствии с макрокомандой машинного описания " PROMOTED_MODE " (* См.: Размещение памяти::.). В этом случае тип " subreg " является объявленным типом объекта и тип " SUBREG_REG " является типом регистра, который содержит объект. Расширяемые ые переменные всегда расширяются нулем или знаком в более широкий тип при каждом присваивании. Сохраняется в поле " in_struct " поле и печатается как " /s ". Отлично от нуля в " subreg " который имеет " SUBREG_PROMOTED_VAR_P " отличным от нуля, если вызываемый объект расширен нулем, и ноль, если он расширен знаком. Сохраняется в поле " unchanging " и печатается как " /u ". Отлично от нуля в " reg " или " mem ", если значение не изменено. (Этот флаг не устанавливается для ссылок на константы через указатели. Такие указатели гарантируют только, что объект будет не изменен явно текущей функцией. Объект может бы быть изменен другими функциями или через совмещение имен.) Сохраняется в поле " unchanging " и печатается как " /u ". Отлично от нуля в insn, если получено из вызова встроенной функции. Сохраняется в поле " integrated " и печатается как " /i ". Может быть удалено; в настоящее время от него ничего не зависит. В " symbol_ref " указывает, что X использован. Обычно используется только для того, чтобы гарантировать, что X только однажды объявлен внешним. Сохраняется в поле " used ". В " symbol_ref " используется как флаг для специфических для машины целей. Сохраняется в поле " volatil " и печатается как " /v ". В выражениях ` label_ref ' отлично от нуля, если это - ссылка на метку, которая находится снаружи самого внутреннего цикла, содержащего ссылку на метку. Сохраняется в поле " in_struct " и печатается как " /s ". В insn отлично от нуля, если insn был удален. Сохраняется в поле " volatil " и печатается как " /v ". В " insn " в слоте задержки ветви insn указывает, что должна использоваться аннулирующая ветвь. См. обсуждение " sequence " ниже. Сохраняется в поле " unchanging " и печатается как " /u ". В " insn " в слоте задержки ветви указывает, что insn - из адреса перехода. Если для ветви insn " INSN_ANNULLED_BRANCH_P " установлен, этот insn должен выполняться, только если переход производится. Для аннулированных ветвей с этим битом, равным 0, insn должен выполняться, если переход не производится. Сохраняется в поле " in_struct " и печатается как " /s ". Отлично от нуля в " symbol_ref " если он ссылается на часть текущего "пула констант" функции. Это адреса, близкие к началу функции, и GNU CC предполагает, что они могут быть адресованы непосредственно (возможно, с помощью базовых регистров). Сохраняется в поле " unchanging " и печатается как " /u ". В " call_insn " указывает, что insn представляет обращение к постоянной функции. Сохраняется в поле " unchanging " и печатается как " /u ". В " code_label " указывает, что метка никогда не может быть удалена. Метки, вызываемые нелокальным переходом, будут иметь этот набор битов. Сохраняется в поле " in_struct " и печатается как " /s ". В течение планирования команд в insn указывает, что предыдущий insn должен планироваться вместе с этим insn. Это используется, чтобы гарантировать, что определенные группы команд не будут разбиваться на части проходом планирования команд, например, " use " insns перед " call_insn " не могут отделяться от " call_insn ". Сохраняется в поле " in_struct " и печатается как " /s ". Вот поля, к которым обращаются вышеупомянутые макрокоманды: Обычно этот флаг используется только в один момент, а именно - в конце генерации RTL функции, чтобы посчитать, сколько раз в insns появляется выражение. Выражения, которые появляются больше чем однажды, копируются, согласно правилам совместного использования структур (* См.: Совместное использование::.). В " symbol_ref " он указывает что внешнее объявление для символа уже было написано. В " reg " он используется регистром листа, перенумеровывающим код, чтобы гарантировать, что каждый регистр перенумеровывается только однажды. Этот флаг используется в выражениях " mem ", " symbol_ref " и " reg " и в insns. В файлах дампа RTL он печатается как " /v ". В выражении " mem " он равен 1, если ссылка памяти переменная. Переменные ссылки памяти нельзя удалять, переупорядочивать и объединять. В выражении ` symbol_ref ', это используется для специфических для машины целей. В выражении ` reg ' он равен 1, если значение - пользовательская переменная, и 0, если это внутренняя временная переменная транслятора. В insn, значение 1 означает, что insn был удален. В выражениях " mem " он равен 1, если данная величина ссылается на весь массив или структуру или их часть ; 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 ". В выражениях " reg " и " mem " 1 означает, что значение выражения никогда не изменяется. В выражениях " subreg " он равен 1, если " subreg " ссылается на объект без знака, чей тип был расширен до более широкого тип. В insn 1 означает, что это - аннулирующая ветвь. В выражении " symbol_ref ", 1 означает, что этот символ адресует что-то в пуле констант функции. В " call_insn " 1 означает, что эта команда - обращение к постояннной функции. В дампе RTL этот флаг представляется как " /u ". В некоторых видах выражений, включая 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" - тип, в котором байт рассматривается как целое. `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"- тип числа с плавающей точкой, имеющего четверную точность (шестнадцать байт). `CCmode' "Condition Code" - тип представляющий значение операций сравнения. Результат сравнения представляется, как набор бит на каждой машине по своему. Этот тип не используется на машинах, использующих `cc0'. `BLKmode' "Block" - тип, представляющий значение которое нельзя представить никаким другим типом. В RTL этот тип используется только для ссылок на память, и только в том сслучае если они используются в командах относяшихся к строкам и векторам. В машинах, не имеющих команд такого типа этот тип вообще не появляется. `VOIDmode' Тип Void значит либо отсутствие типа либо неопределенный тип. Например, RTL-выражение `const_int' имеет тип VOIDmode, потому что его тип зависит от контекста. В отладочной информации RTL выражения типа `VOIDmode' обозначаются отсутствием какого бы то ни было типа. `SCmode, DCmode, XCmode, TCmode' Этот тип комплексных чисел, представляемый парой чисел с плавающей точкой. Значение числа с плавающей точкой будет соответственно типа `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-битных структур транслятор использует `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'. Далее идут некоторые макросы, относящиеся к типам: `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. `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' равен единице то `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'. `(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'. Количество бит является машинно-зависимым и обычно оно определено в команде, которая инициализирует количество старших битов регистра. Эта команда используется с `lo_sum' для представления типичных последовательностей с двумя командами, используемых в RISC машинах, для согласования расположения в глобальной памяим. M должно быть типа `Pmode'. 4.6. Регистры и память Ниже перечислены типы выражений для доступа к машинным регистрам и основной памяти. `(reg:M N)' Для небольших значений целого числа N (меньше, чем `FIRST_PSEUDO_REGISTER') это будет ссылка на машинный регистр с номером N - "аппаратный регист". Для больших значений числа N, возвращается ссылка на временное значение или "псевдо регистр". Принцип действия компилятора - создавать код, предполагая неограниченное число таких псевдо регистров, а поже заменять их на аппаратные регистры или на ячейки памяти. M - тип ссылки. Это необходимо, т.к. обычно машина может использовать каждый регистр более чем в одном режиме. Например могут существовать инструкции, которые используют регистр, содержащий слово, как полслова или как байт, более того, могут быть инструкции, ссылающиеся на этот регистр как на число с плавающей точкой различной точности. Тип необходимо указывать даже для регистра, который машина может использовать только одним способом. Символ `FIRST_PSEUDO_REGISTER' определяется в соответствии с описанием машины, т.к. так как число аппаратных регистров на машине - инвариантная характеристика машины. Однако, обратите внимание, что не все машинные регистры обязаны быть регистрами общего назначения. Все машинные регистры, которые можно использовать для хранения данных учтены в числе аппаратных регистров, даже те, которые могут использоваться только в некоторых коммандах или содержать только орпеделенные типы данных. All К аппаратным регистром можно обращатся в различных режимах в течении одной функции, но к псевдо регистру можно обращаться только в режиме, в котором он был описан. Если необходино описать доступ к псевдо регистру в другом режиме используйте выражение `subreg'. Выражение 'reg' типа M, которое содржит более одного слова данных, может на самом деле состоять из нескольких последовательных регистров. Если кроме того номер регистра соответствует аппаратному регистру, то фактически он представляется несколькими последовательными аппаратными регистрами, начиная с данного. Каждый номер псевдо регистра, используеммого коде RTL-функции представляется уникальным выражением `reg'. Некоторые номера псевдо регистров, от `FIRST_VIRTUAL_REGISTER' до `LAST_VIRTUAL_REGISTER' появляются только в фазе RTL и уничтожаются перед фазой оптимизации. Пока не завершено RTL образование функций, они представляют расположение в стеке, которое не может быть определено пока не завершится RTL-генерация функций. Определены следующие номера для регистров: `VIRTUAL_INCOMING_ARGS_REGNUM' Указывает на первое слово аргументов, переданных в стек. Обычно это аргументы, переданные вызывающей программой, но вызывающая программа может положить в стек некоторые аргументы, которые до этого были в регистрах. Когда RTL-фаза завершена, этот виртуальный регистр замещается суммой регистра, полученного с помощью `ARG_POINTER_REGNUM' и значения `VIRTUAL_STACK_VARS_REGNUM' Если `FRAME_GROWS_DOWNWARD' определен, то это указывает на место сразу после первой переменной в стеке. Иначе, это указывает на первую переменную в стеке. `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' используется для обращения к машинному регистру в режеме, отличном от естственного, или что бы обратится к мульти-регистру Каждый псевдо регистр имеет свой естественный режим. Если необходимо работать с этим регистром в другом режиме - например, выполнить команду перемещения слова над регистром, который содержит один байт, псевдо регистр должен быть включен в 'subreg'. В этом случае WORDNUM - ноль. Обычно размер M по крайней мере не больше размера типа REG, тогда это ограничивает рассмотрение только тех бит REG, которые находятся в M. Иногда разме M больше чем размер типа REG. Тогда эти выражения `subreg' часто называются "парадоксальными". Они используются в случаях, когда мы хотим обратиться к объекту в режиме с большим размером, но не заботиться, какое значение имеют дополнительные биты. Предварительный проход гарантирует, что "парадоксальные" ссылки сделаны только на аппаратные регистры. Еще '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'. Если бы например номер регистра был бы 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)' может быть корректно использована только в двух контекстах: как адрес перехода (в операторах тестов и сранения) когда источник - оператор сравнения, и как первый оператор `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'.. Все инструкции, которые не выполняют переход, изменяют счетчик программы неявно, увеличивая его, но упоминать это в 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 с целью сравнения. Результат вычисляется без переполнения, как если бы был с бесконечной точностью. Конечно, машина не может вычитать с бесконечной точностью. Однако, когда нужен только знак, она может симулировать как будто делает это. И единственный способ, которым это выражение может быть корректно использоваться - это сохранение его в коде условия. Тип М не связан с типами X и Y, а является типом значения кода условия. Если используется `(cc0)', то это `VOIDmode'. Иначе это какой-то тип класса `MODE_CC', обычно `CCmode'. см. Код условия. Обычно X и Y должны иметь один и тот же тип. иначе сравнение корректно только в случае, когда тип X принадлежит классу `MODE_INT' а Y - `const_int' или `const_double' с типом `VOIDmode'. Тип X определяет в какком режиме будет выполнено сравнение, поэтому он не моднт быть Если один из операндов - константа, то она должна быть вторым аргументом и тип сравнения корректируется как соответствующий. Сравнение двух констант некорректно, так как невозможно узнать режим в котором проводить сравнение. Сравнение должно также быть свернуто в течение трансляции, или первый операнд должен быть загружен в регистр, в то время пока режим еще известен. `(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, которые не обязаны быть одинаковыми. Для расширяющего умножения без знака аналогично, только исспользуя `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, который должен быть режимом с фиксированной точкой. `(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)' Возвращает один плюс индекс младшего значащего бита в Х, представленное как целое число типа М. (Если Х-ноль, то возвращается ноль.) Тип Х не обязательно должен быть М; в зависимости от целевой машины, могут быть корректными различные комбинации типов. 4.7. Операции сравнения Операторы сравнения проверяют отношение двух операндов и должны представлять машинно-независимое ненулевое значение, описываемое с помошью `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, но не беззнаково больше. Действительно, -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. На большинстве машин, выражения ` if_then_else ' корректны только, чтобы выразить условные переходы. `(cond [TEST1 VALUE1 TEST2 VALUE2 ...] DEFAULT)' Аналогично 'if_then_else', но более обще. Каждый из TEST1, TEST2,... выполняется в свою очередь. Результат этого выражения - VALUE, соответствующее первому тесту отличному от нуля, или DEFAULT, если все тесты ноль. Пока для образцов инструкций это еще некорректно и поддерживается только для insn атрибутов. см. Атрибуты Insn 4.8. Битовые поля. Существуют специальные выражения для представления конструкций, т.е. они использующих битовые поля. Эти выражения в 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)' Как `sign_extract', но ссылается на беззнаковое или нуль-расширенное битовое поле. Извлекается та же самая последовательность битов, но она заполняет целое слово с нулями вместо знако-расширения. 4.9. Преобразования Все преобразования типов должны быть представлены явными операциями преобразования. Например, выражение, которое является суммой байта и слова не может быть описана следующим образом: `(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. `(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.10. Объявления Выражения объявлений не представляют собой арифметические операции, но устанавливают утверждения относительно операндов: `(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.11. Выражения с побочным эффектом Коды выражения, описанные до сих пор, представляют значения, а не действия. Но машинные команды никогда не производят значения; они значимы только из-за их побочных эффектов на состоянии машины. Специальные коды выражения используются, чтобы представить побочные эффекты. Тело команды - всегда один из этих кодов побочного эффекта; коды, описанные выше, которые представляют значения, появляются только как операнды этих. Представляет действие сохранения значения 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) " (для случая, когда нет перехода), а другой из них должен быть " label_ref " (для случая, когда есть переход). X может также быть " mem " или " (plus: SI (pc) Y) ", где Y может быть " reg " или " mem "; эти необычные варианты используются для осуществления переходов через таблицы ветвлений. Если LVAL не является ни " (cc0) ", ни " (pc) ", то тип LVAL должен не быть " VOIDmode " и тип X должен подходить для типа LVAL. К LVAL обычно обращаются макрокомандой " SET_DEST ", а к X - макрокомандой " SET_SRC ". Единственное выражение в образце, представляющее возврат из текущей функции, на машинах, где это может быть выполнено одной командой, типа Vaxes. На машинах, где для возвращения из функции должен быть выполнен многокомандный "эпилог" , возврат выполняется переходом к метке, которая предшествует эпилогу, и код выражения " return " никогда не используется. Внутри выражения " if_then_else " представляет значение, которое должно быть помещено в " pc " для возврата в вызывающую функцию. Обратите внимание, что образец insn " (return) " логически эквивалентен " (set (pc) (return)) ", но последняя форма никогда не используется. Представляет обращение к функции. FUNCTION - выражение " mem ", чей адрес - адрес функции, которую нужно вызвать. NARGS - выражение, которое может использоваться для двух целей: на некоторых машинах оно представляет число байтов параметров в стеке; на других, оно представляет число регистров с параметрами. Каждая машина имеет стандартный машинный тип, который должна иметь FUNCTION. Машинное описание определяет макрокоманду " FUNCTION_MODE ", преобразующуюся в необходимое имя типа. Цель этого типа - указать доступные виды адресации на машинах, где доступные виды адресации зависят от машинного типа. Представляет сохранение или возможное сохранение непредсказуемого, неописанного значения в 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. Однако, фаза перезагрузки может распределять регистр, используемый для одного из вводов, если для выбранного варианта не указано ограничение " & " (* См.: Модификаторы::.). Вы можете применять " clobber " для аппаратного регистр, псевдорегистра или выражения " scratch "; в последних двух случаях, GNU CC распределит доступный там аппаратный регистр для временного использования. Для команд, которые требуют временного регистра, Вы должны использовать " scratch " вместо псевдорегистра, потому что это позволит фаза комбинирования добавить " clobber " когда требуется. Это делается кодированием (" clobber " (" match_scratch " ...)). Если Вы используете " clobber " для псевдорегистра, используйте тот, который больше нигде не появляется и генерируйте каждый раз новый. Иначе могут смешаться CSE. Имеется другое известное использование затирания псевдорегистра в " parallel ": когда один из входных операндов insn также затирается insn. В этом случае, использование одного и того же псевдорегистра для затирания и в другом месте в insn приводит к ожидаемым результатам. Представляет использование значения X. Это указывает, что значение X в этой точке программы необходимо, даже если это может быть неочевидно. Следовательно, транслятор не будет пытаться удалять предыдущие команды, которые приводят только к сохранению значения X. X, должен быть выражением " reg ". В течение фазы планирования отсроченных переходов X может быть insn. Это указывает, что X предварительно был размещен в этом месте кода и зависимость его данных должна быть принята во внимание. Эти " use " insns будут удалено перед окончанием фазы планирования отсроченных переходов. Представляет несколько побочных эффектов, выполняемых параллельно. Квадратные скобки обозначают вектор; операнд " parallel " является вектором из выражений. X0, X1 и так далее - индивидуальные выражения с побочным эффектом - выражения кода " set ", " call ", " return ", " clobber " или " use ". " 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 если Вы определяете какую-либо решетчатую оптимизацию. Представляет последовательность 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, хотя, строго говоря, они не всегда описывают побочные эффекты как таковые: Представляет литеральный код ассемблера с помощью строки S. Представляет машинно-специфическую операцию над OPERANDS. INDEX задает машинно-специфическую операцию. " unspec_volatile " используется для volatile операций и операций, которые могут захватывать; " unspec " используется для остальных операций. Эти коды могут появляться внутри " pattern " insn, внутри " parallel " или внутри выражения. Представляет таблицу адресов перехода. Векторные элементы LR0,LR1 и т.д., являются выражениями " label_ref ". Тип M определяет, сколько места выделяется каждому адресу; обычно M должен быть " Pmode ". Представляет таблицу адресов перехода, выраженных как смещения от BASE. Векторные элементы LR0,LR1 и т.д. являются выражениями " label_ref ", и BASE тоже.Режим M определяет, сколько места выделяется каждому адресу. 4.12. Вложенные побочные эффекты на адресах Четыре специальных кода выражения побочного эффекта появляются как адреса памяти. Представляет побочный эффект уменьшения X на стандартное число, а также значение, которое X имеет после уменьшения. X должен быть " reg " или " mem ", но большинство машин позволяет только " reg ". М должен быть машинным типом для указателей,используемым на машине. Число, на которое уменьшается X - длина в байтах машинного типа ссылки памяти, для которой это выражение служит адресом. Вот пример использования: (mem: DF (pre_dec: SI (reg: SI 39))) Это означает уменьшение псевдорегистра 39 на длину значения " DFmode " и использование результата для адресации значения " DFmode ". Аналогично, происходит не уменьшение, а увеличение X. Представляет тот же самый побочный эффект, что и " pre_dec ", но другое значение. Значение, представляемое здесь - значение X перед уменршением. Аналогично, происходит не уменьшение, а увеличение X. Эти вложенные выражения побочного эффекта должны использоваться осторожно. Образцы команд не могут использовать их. До прохода " flow " транслятора, они могут иметь место только для помещения значений на стек. Проход " flow " находит случаи, когда регистры увеличиваются или уменьшаются в одной команде и используются как адрес непосредственно перед этим или после этого; эти места преобразуются для использования pre- или post- увеличения или уменьшения. Если регистр, используемый как операнд этих выражений, используется для другого адреса в insn, используется первоначальное значение регистра. Использование регистра вне адреса внутри того же самого insn не разрешается, так же, как и использование в выражении с вложенным побочным эффектом, потому что такие insns ведут себя по-разному на разных машинах и, следовательно, должны обрабатываться неоднозначно и запрещаются. Команду, которая может быть представлена при помощи вложенного побочного эффекта, можно также представить, используя " parallel ", содержащий дополнительный " set ", чтобы описать, как изменяется регистр адреса. Это не сделано, потому что машины, которые вообще позволяют эти операции, обычно позволяют их всюду, где запрашивается адрес памяти. Описание их как дополнительных сохранений " parallel " потребовало бы удвоения числа входов в машинном описании. 4.13. Команды Ассемблера как выражения RTX код " asm_operands " представляет значение, получившееся из определенной пользователем команды ассемблера. Это используется для представления операторов " asm " с параметрами. Операторы " asm " с одиночным операндом вывода, типа этого: asm ("foo %1, %2, %0" : "=a" (outputvar): "g" (x + y), "di" (*z)); представляются с использованием одиночного " 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.14. Insns RTL представление кода функции - связанная в две стороны цепочка объектов, называемых "insns". Insns - выражения со специальными кодами, которые не используются ни для какой другой цели. Некоторые insns фактически являются командами; другие представляют таблицы управления для операторов " switch "; другие представляют метки перехода или различных видов описаний. В дополнение к собственным специфическим данным, каждый insn должен иметь уникальный идентифицирующий номер, который отличает его от всех других insns в текущей функции (после отсроченного планирования перехода копии insn с одинаковыми идентифицирующими номерами могут присутствовать в нескольких местах в функции, но эти копии всегда будут идентичны и будут появляться только внутри " sequence "), и указатели на предыдущий и следующий insn для получения цепочки. Эти три поля занимают одну и ту же позицию в каждом insn, независимо от кода выражения insn. Вообще говоря, к ним можно обращаться при помощи " XEXP " и " XINT ", но вместо них всегда используется три специальных макрокоманды: Обращается к уникальному идентификатору insn I. Обращается к указателю цепочки на insn, предшествующий I. Если I - первый insn, это - нулевой указатель. Обращается к указателю цепочки на 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, содержащий выражение " sequence ", а значение " PREV_INSN( NEXT_INSN (INSN)) " - это insn, являющийся последним в выражении " sequence ". Вы можете использовать эти выражения, чтобы найти выражение " sequence ", содержащее данный insn. Каждый insn имеет один из следующих шести кодов выражения: Код выражения " insn " используется для команд, которые не делают переходов и обращений к функциям. Выражения " sequence " всегда содержатся в insns с кодом " insn ", даже если один из их insns делает переход или обращение к функции. Insns с кодом " 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 " insns имеют те же самые дополнительные поля, что и " insn " insns, обращение к которым производится таким же образом, и, кроме того, содержат поле " CALL_INSN_FUNCTION_USAGE ", который содержит список (цепочка выражений " expr_list "), содержащий " use " и " clobber " выражения, которые обозначают аппаратные регистры, используемые или затираемые вызываемой функцией. Регистр, указанный в " clobber " в этом списке, изменяется *после* выполнения " call_insn ", в то время как регистр в " clobber " в теле " call_insn " затирается прежде, чем insn завершает выполнение. " clobber " выражения в этом списке увеличивают регистры, указанные в " CALL_USED_REGISTERS " (* См.: Основные сведения о регистрах::.). " code_label " insn представляет метку, на которую возможен переход при помощи insn перехода. Он содержит два специальных поля данных в дополнение к трем стандартным. " CODE_LABEL_NUMBER " используется для содержания " label number ", номер, который однозначно идентифицирует эту метку среди всех меток в трансляции (не только в текущей функции). В конечном счете, метка представляется на ассемблерном выводе как метка ассемблера, обычно в форме " LN ", где N - номер метки. Когда " code_label " появляется в выражении RTL, оно обычно появляется внутри " label_ref ", которое представляет адрес метки как номер. Поле " LABEL_NUSES " определяется только после завершения фазы оптимизации перехода и содержит количество ссылок на эту метку в текущей функции. Барьеры помещаются в поток команд, если управление не может передаваться коду после них. Они помещаются после команды безусловного перехода, чтобы указать, что переходы являются безусловными, и после обращений к функциям " volatile ", которые не возвращаются (например, " exit "). Они не содержат никакую информацию, кроме трех стандартных полей. " note " insns используются для представления дополнительной отладочной и декларативной информации. Они содержат два нестандартных поля, целое число, к которому обращаются макрокомандой " NOTE_LINE_NUMBER " и строку, к которой обращаются " NOTE_SOURCE_FILE ". Если " NOTE_LINE_NUMBER " положительно, примечание представляет номер исходной строки, а " NOTE_SOURCE_FILE " - имя исходного файла, содержащего эту строку. Эти примечания управляют генерацией номеров строк в ассемблерном выводе. В противном случае, " NOTE_LINE_NUMBER " не реальный номер строки, а код с одним из следующих значений (а " NOTE_SOURCE_FILE " должен содержать нулевой указатель): Такие примечания полностью игнорируются. Некоторые проходы транслятора удаляют insns, преобразуя их в примечания этого вида. Эти типы примечаний указывают позицию начала и конца уровня обзора имен переменных. Они управляют выводом информации об отладке. Эти типы примечаний указывают позицию начала и конца циклов "while " и " for ". Они позволяют оптимизатору циклов быстро находить циклы. Появляется в месте цикла, на которое делает переход оператор " continue ". Это примечание указывает место в цикле, где начинается проверка на выход для тех циклов, в которых проверка на выход продублирована. Эта позиция становится другим виртуальным началом цикла при рассмотрении инвариантов цикла. Появляется около конца тела функции, непосредственно перед меткой, на которую переходит оператор " return " (на машинах, на которых одиночной команды не достаточно для возврата). Это примечание может быть удалено оптимизацией перехода. Появляется после каждого обращения к " setjmp " или соответствующей функции. Эти коды печатаются символически, когда они появляются в отладочных дампах. Машинный тип insn - обычно " VOIDmode ", но некоторые фазы используют тип для различных целей; например, проход перезагрузки устанавливает его в " HImode ", если insn требуется перезагрузка, но не удаление регистров, и " QImode ", если требуется и то, и другое. Проход общего удаления подвыражений устанавливает тип insn в " QImode ", если это первый insn в блоке, который уже был обработан. Вот таблица дополнительных полей " insn ", " jump_insn " и " call_insn " insns: Этим 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, или -1, если соответствие еще не устанавливалось. Такое соответствие никогда не устанавливается и это поле остается -1 для insn, чей образец состоит из одиночного выражения " use ", " clobber ", " asm_input ", " addr_vec " или " addr_diff_vec ". Соответствие также никогда не устанавливается для insns, которые получаются из операторов " asm ". Они содержат по крайней мере одно выражение " asm_operands ". Функция " asm_noperands " возвращает неотрицательное значение для таких insns. При отладочном выводе это поле печатается как число с последующим символическим представлением, которое определяет месторасположение образца в " md " файле так некоторое маленькое положительное или отрицательное смещение от указанного образца. Список (цепочка выражений " insn_list ") предоставляет информацию о зависимостях между командами внутри базисного блока. Между связанным insns не может быть ни перехода, ни метки. Список (цепочка выражений " expr_list " и " insn_list ") предоставляет различную информацию относительно insn. Часто это информация относительно регистров, используемых в этом insn. Поле " LOG_LINKS " insn - это цепочка " insn_list " выражений. Каждое из них имеет два операнда: первое - insn, а второе - другое выражение " insn_list " (следующее в цепочке). Последний " insn_list " в цепочке имеет нулевой указатель в качестве второго операнда. Существенно, какие insns появляются в цепочке как первые операнды выражений " insn_list ". Их порядок не имеет значения. Этот список первоначально устанавливается проходом потокового анализа; до него это нулевой указатель. Потокоый анализ только добавляет связи для тех зависимостей данных, которые могут использоваться для комбинации команд. Для каждого insn проход потокового анализа добавляет связь с insns, которые сохраняют в регистры значения, которые используются впервые в этом 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: Значение в OP умирает в этом insn; то есть изменение значения немедленно после этого insn не воздействовало бы на дальнейшее поведение программы. Это не обязательно означает, что регистр OP не содержит никакого полезного значения после этого insn, так как это может также быть вывод insn. В таком случае, однако, " REG_DEAD " примечание было бы избыточно и обычно не представляется до окончания прохода перезагрузки, но никакой код не использует этот факт. Регистр OP увеличивается (или уменьшается; на этом уровне не имеется никакого различия) вложенным побочным эффектом внутри этого insn. Это означает, что он появляется в выражении " post_inc ", " pre_inc ", " post_dec " или " pre_dec ". Регистр OP имеет неотрицательное значение, когда этот insn достигается. Это используется так, что команды декремента и перехода до нуля, типа m68k dbra, могут быть согласованы. " REG_NONNEG " примечание добавляется к insns, только если машинное описание имеет образец " decrement_and_branch_until_zero ". Этот insn не вызывает конфликта между OP и элементом, устанавливаемым этим insn, даже если имеет такую возможность. Другими словами, если регистру-адресату и OP мог бы быть иначе присвоен один и тот же регистр, этот insn не предотвращает этого присваивания. Insns с этим примечанием - обычно часть блока, который начинается с a " clobber " insn, указывающего псевдорегистр из нескольких слов (который будет выводом блока), группа insns, каждое из которых устанавливает одно слово значения и имеет примечание " REG_NO_CONFLICT ", и заключительное insn, которое копирует вывод к себе с примечанием " REG_EQUAL ", вычисляя выражение. Этот блок инкапсулируется примечаниями " REG_LIBCALL " и " REG_RETVAL " для первого и последнего insns, соответственно. Этот insn использует OP, " code_label ", но - не " jump_insn ". присутствие этого примечания позволяет оптимизации перехода знать что OP, на самом деле, используется. Следующие примечания описывают атрибуты выводов insn: Это примечание имеет силу только для insn, которое устанавливает только один регистр и указывает, что этот регистр будет равен 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 " выражение для расположения параметра на стеке), которое может использоваться вместо регистра, если доступно недостаточное количество регистров. Кроме расположения параметров в стеке, которое указывается примечанием " REG_EQUIV " и не используется при проходах ранней оптимизации и псевдорегистров, которые являются эквивалентными ячейке памяти в течение всего времени своего существования, которые пока не обнаружены при трансляции, все эквивалентности первоначально обозначаются примечанием " REG_EQUAL ". На ранних стадиях распределения регистров примечание " REG_EQUAL " изменяется на примечание " REG_EQUIV ", если OP - константа, и insn представляет единственый набор его регистров назначения. Таким образом, проходы транслятора до распределения регистров должны проверять только примечания " REG_EQUAL ", а проходы, следующие после распределения регистров, должны проверять только примечания " REG_EQUIV ". Регистр OP, устанавливаемый этим insn, не будет использоваться в последующих insn. Это отличается от примечания " REG_DEAD ", которое указывает, что значение во вводе не будет использоваться впоследствии. Эти два примечания независимы и могут оба присутствовать для одного и того же регистра. Одиночный вывод этого insn, содержащий ноль перед этим insn. OP - insn, устанавливающий его в ноль. Вы можете полагаться на это примечание, если оно присутствует и OP не был удален или превращен в примечание ; его отсутствие не подразумевает ничего. Эти примечания описывают связи между insns. Они встречаются парами: одно insn имеет одно примечание, которое указывает на другое insn, которое имеет обратное примечание, указывающее обратно на первое insn. Этот insn копирует значение последовательности из многих insn (например, вызова библиотеки), и OP - первый insn последовательности (для вызова библиотеки - первый insn, который был сгенерирован для установки параметров вызова библиотеки). Оптимизация циклов использует это примечание для обработки такой последовательности, как одиночная операция для перемещения кода, а потоковый анализ использует это примечание, чтобы удалить последовательности, результаты которых "мертвы". Примечание" REG_EQUAL " будет также обычно присоединяться к этому insn для обеспечения выражения, вычисляемого последовательностью. Это - инверсия " REG_RETVAL ": оно помещается в первый insn последовательности из многих insn и указывает на последний. На машинах, которые используют " cc0 ", insns, которые устанавливают и используют " cc0 ", установка и использование " cc0 " являются смежными. Однако по окончании заполнения слотов задержки перехода это уже может не быть так. В этом случае примечание " REG_CC_USER " будет помещено в insn, устанавливающий " cc0 ", для указания insn, использующего " cc0 ", а примечание " REG_CC_SETTER " будет помещено в insn, использующий " cc0 ", для указания insn, устанавливающего " cc0 ". Эти значения используются только в поле " LOG_LINKS ", и указывают тип зависимости, которую представляет каждая связь. Связи, которые указывают зависимость данных (типа зависимости чтение после записи) не используют никакого кода, они просто имеют тип " VOIDmode " и печатаются без всякого описательного текста. Указывает антизависимость (типа зависимости запись после чтения). Указывает зависимость вывода (типа зависимости запись после записи). Для удобства, машинный тип в " insn_list " или " expr_list " печатается в отладочных дампах с использованием этих символических кодов. Единственое различие между кодами выражения " insn_list " и " expr_list " - то, что первый операнд " insn_list " считается insn и печатается в отладочных дампах как уникальный идентификатор insn; первый операнд " expr_list " печатается обычным способом как выражение. 4.15. 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 ", это обрабатывается путем передачи подпрограмме адреса места, в которое следует записать значение. Так что само 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.16. Предположения о Совместном Использовании Структур Транслятор считает, что некоторые виды выражений RTL уникальны; не существует двух различных объектов, представляющих одно и то же значение. В других случаях он делает противоположное предположение: никакой объект RTL выражения определенного вида не появляется более чем в одном месте в объемлющей структуре. Эти предположения относятся к одиночной функции; кроме RTL объектов, которые описывают глобальные переменные и внешние функции, и нескольких стандартных объектов типа маленьких целочисленных констант, никакие объекты RTL не являются общими для двух функций. ╥ Каждый псевдорегистр имеет только один объект " 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 без нежелательных побочных эффектов для других insns. ╥ В течение начальной генерации RTL общедоступнная структура представлена свободно. После того, как весь RTL для функции будет сгенерирован, вся общедоступнная структура копируется " unshare_all_rtl " в " emit-rtl.c ", после чего гарантируется следование вышеупомянутых правил. ╥ В течение прохода комбинирования общедоступнная структура внутри insn может существовать временно. Однако, общедоступнная структура копируется перед окончанием комбинирования для insn. Это делается вызовом " copy_rtx_if_shared ", который является подпрограммой " unshare_all_rtl ". 4.17. Чтение RTL Чтобы читать объект RTL из файла, вызовите " read_rtx ". Ему требуется один параметр, поток стандартного ввода, а возвращает он один объект RTL. Чтение RTL из файла происходит очень медленно. Это не проблема в настоящее время, поскольку чтение RTL происходит только как часть формирования транслятора. Люди часто представляют использование RTL, сохраненного как текст в файле, как интерфейс между внешним интерфейсом языка и большей частью GNU CC. Эта идея не выполнима. GNU CC был разработан, чтобы использовать RTL только внутренне. Корректный RTL для данной программы очень сильно зависит от конкретной целевой машины. И RTL не содержит всей информации о программе. Соответствующий способ связывать с помощью интерфейса GNU CC на новый внешний интерфейс языка Со структурой данных "дерева". Не имеется никакого руководства для этой структуры данных, но она описана в файлах " tree.h " и " tree.def ". 5. Машинные Описания Машинное описание имеет две части: файл образцов команд ( ".md" файл) и файл заголовка C макроопределений. ".md" файл для целевой машины содержит образец для каждой команды, поддерживаемой целевой машиной (или, по крайней мере, для каждой команда, о которой стоит сообщать транслятору). Он может также содержать комментарии. Часть строки после точки с запятой является комментарием, если точка с запятой не находится внутри строки, ограниченной кавычками. См. следующую главу для информации о файле заголовка C. 5.1. Все про Образцы Команд Каждый образец команды содержит незавершенное выражение RTL, с частями, которые будут заполнены позже, ограничениями на операнды, которые говорят,как могут быть заполнены эти части, и образец вывода или C код, чтобы сгенерировать вывод ассемблера, который полностью выполняется в выражении " define_insn ". " define_insn " является выражением RTL, содержащим четыре или пять операндов: 1. Опциональное имя. Присутствие имени указывает, что этот образец команды может выполнять некоторую стандартную работу для проход генерации RTL транслятора. Этот проход знает определенные имена и использует образцы команд с этими именами, если имена определены в машинном описании. Отсутствие имени обозначется пустой строкой на том месте, где должно быть имя. Безымянные образцы команд никогда не используются для генерации кода RTL, но они могут позволять нескольким более простым insns объединяться впоследствии. Имена, которые не определены таким образом и используются в генерации RTL, не имеют эффекта; они эквивалентны отсутствию имени вообще. 2. " RTL шаблон " (* См.: RTL Шаблон::.) - вектор из незавершенных выражений RTL, которые показывают, как команда должна выглядеть. Они незавершенные, потому что могут содержать " match_operand ", " match_operator " и " match_dup " выражения в позициях для операндов команды. Если вектор имеет только один элемент, этот элемент - шаблон для образца команды. Если вектор имеет несколько элементов, то образец команды - выражение " parallel ", содержащее описанные элементы. 3. Условие. Это - строка, которая содержит выражение C, которое является тестом для окончательного решения, соответствует ли тело insn этому образцу. Для именованного образца, условие (если оно есть) не может зависеть на данных в согласовываемом insn, а может зависеть только от флагов целевой машины. Транслятор должен проверять эти условия во время инициализации, чтобы точно знать, какие поименованные команды доступны для частичного запуска. Для безымянных образцов условие применяется только тогда, когда происходит согласование для индивидуального insn, и только после установления соответствовия insn шаблону распознавания образца. Операнды insn могут быть получены из вектора " operands ". 4. " Шаблон вывода ": строка, которая говорит, как выводить insns как код ассемблера. "%" в этой строке определяет, куда подставлять значения операнда. * См.: Шаблон Вывода::. Когда простой подстановки недостаточно, Вы можете делать вставку из кода для получения вывода. * См.: Оператор Вывода::. 5. Опционально, вектор, содержащий значения атрибутов для insns, соответствующих этому образцу. * См.: Атрибуты 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.3. RTL Шаблоны RTL шаблон используется, чтобы определить, какие insns соответствуют конкретному образцу и как найти их операнды. Для названных образцов, RTL шаблон также показывает, как создать insn из данных операндов. Конструкция включает замену определенных операндов в копию шаблона. Соответствие включает определение значений, которые служат как операнды в согласовываемом 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 управляет перезагрузкой и выбором наилучшего класса регистров для хранения значений. ( Подробнее это будет объяснено ниже (см. ограничения::.) ). Людям часто не ясны различия между ограничениями и предикатами. Предикат помогает решить, соответствует ли данный insn образцу. Ограничения не играют никакой роли в этом решении, но они управляют дальнейшими решениями в случае, когда insn соответствует. На CISC машинах, наиболее распространенный PREDICATE - это константой, регистром или ячейкой памяти, и корректен ли операнд для режима M. Для операнда, который должен быть регистром, PREDICATE должен быть не-регистры через регистры, но это заставляло бы делать 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', чьи операнды являются либо аппаратным регистром либо необходимо. `(match_dup N)' Это выражение - также placeholder для операнда с номером N, оно используется, когда операнд должен появиться в insn больше чем один раз. При конструировании, 'match_dup' действует точно так же как соответствии, '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) 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. (Это выполнено двумя коммутативным выражением: используйте '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, чтобы узнать, может ли он связать эти регистры. Не существует способа определить ограничения в 'match_operator' . Операнд insn, который соответствует match_operator' никогда не имеет никаких ограничений, потому что он никогда не перезагружается как целое. Однако, если части его OPERANDS соответствуют 'match_operand' образцам, эти части могут иметь ограничения на них самих. `(match_op_dup:M N[OPERANDS...])' Подобен 'match_dup', за исключением того, что применяется к операторам вместо операндов. При построении insn, операнд номер N на этом месте будет заменяться. Но в соответствии, 'match_op_dup' ведет себя по-другому. Он принимает, что операнд номер N уже был определен соответствует только идентично выглядящему выражению. `(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")) (use (reg:SI 179)) (clobber (reg:SI 179))])] "" "loadm 0,0,%1,%2") Этот пример взят из 'a29k.md'. Функция 'load_multiple_operations' определена в 'a29k.c' и проверяет, что последующие элементы в `par╜ allel' те же, что и `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 Этот комплекс выражений - placeholder для операнда номер N в инструкции "загрузки адреса": операнд, который определяет ячейку памяти обычным способом, но для которого фактическое значение операнда есть адрес ячейки, а не ее содержание. Выражения `address' никогда не появляются в RTL коде, только в машинных описаниях. И они используются только в машинных описаниях, которые не используют особенности ограничения операнда. Когда ограничения операнда используются, символ 'p' в ограничении служит этой цели. M - тип *адресуемой ячейки памяти*, а не тип адреса. Этот тип всегда один и тот же на данной целевой машине ( 'Pmode' , который обычно является 'SImode'), так что не имеет смысла это упоминать; таким образом, в выражении 'address' не написан никакой тип. Если однажды будет добавлена поддержка машин, в которых адреса объектов различного вида появляются по-разному или используются по-разному (типа PDP-10), различные форматы, возможно, будут нуждаться в различных типах, и эти типы должны быть упомянуты в выражении 5.4. Шаблоны вывода и замена операндов ассемблерный код для образца команды. Почти весь шаблон - фиксированная строка, которая выводится буквально. Символ '%' используется, чтобы определить место замены операндом; он может также использоваться, чтобы обозначать места, где различные варианты ассемблера требуют различного синтаксиса. В самом простом случае, '%', а за ним число N указывает, что надо вывести операнд N в этом месте. альтернативным способом. Четыре символа имеют стандартные, встроенные значения, описанные ниже. Машинная макрокоманда описания 'PRINT_OPERAND' может определять дополнительные символы с нестандартными значениями. постоянным значением без синтаксиса, который обычно указывает непосредственный операнд. заменяется на минус ее. это была ссылка на ячейку памяти, фактическим операндом, обрабатываемым как адрес. Это может быть полезно, когда выводится инструкция "загрузить адрес", потому что часто синтаксис ассемблера для такой команды требует, чтобы Вы писали операнд, как будто это была бы ячейка память. полезно для создания локальных меток, которые нужно упомянуть в шаблоне больше чем один раз, генерирующем многократные команды ассемблера. операнд. Стандартным является только один случай : ' %% ' - выводит в код ассемблера '%' . Другие, нестандартные случаи могут быть определены в макрокоманде '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, а затем вывести полученные команды по отдельности. Это помогает устранять избыточность в шаблонах вывода. Если Вы имеете 'define_insn' который должен создать несколько команд ассемблера, и имеется уже определенное соответствие - 'define_split' , то Вы можете просто использовать '*' как шаблон вывода, вместо того, чтобы создавать шаблон вывода, генерящий несколько ассемблерных команд. Если 'ASSEMBLER_DIALECT' определен, Вы можете использовать в шаблонах конструкции типа '{option0 | option1 | option2} '. Они описывают множество вариантов синтаксиса языка ассемблера. см. Вывод команд 5.5. Операторы С для вывода ассемблера Часто одиночная фиксированная строка шаблона не может производить во всех случаях, подходящих под одиночный образец команды, правильный и эффективный код ассемблера. Например, коды операции могут зависеть от вида операндов; или некоторые неудачные комбинации операндов могут требовать дополнительных машинных команд. Если строка управления вывода начинается с '@' , то это - ряд шаблонов, каждый на отдельной строке. (Пустые строки, ведущие пробелы и знаки табуляции игнорируются.) Шаблоны соответствуют вариантам ограничения образца (см. Множественные альтернативы). Например, если целевая машина имеет двухадресную команду сложения 'addr' для сложения в регистр и другую образец: (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' , чтобы возвратить строку шаблона, которую Вы хотите. Большинство таких шаблонов использует строчные литералы C, которые должны быть разграничены символами двойных кавычек. Перед каждым символом двойных кавычек надо ставить '\' . Операнды могут быть найдены в массиве 'operands' ,тип данных которого в С- ' rtx [] ' . Общепринято выбирать различные способы генерирования ассемблерного кода, базирующихся на том, находится ли непосредственный операнд внутри некоторого диапазона. Будьте внимательным при выполнении этого, потому что результат большее количество битов в 'int' чем целевая машина имеет в типе, в котором константа будет использоваться, то некоторые из битов, которые Вы получаете из 'INTVAL', будут лишними. Для правильных результатов, Вы должны тщательно игнорировать значения этих битов. Возможно вывести команду ассемблера а затем продолжать выводить или вычислять остальные команды, используя подпрограмму 'output_asm_insn'. Она получает два параметра: строка шаблона и вектор операндов. Вектор может быть 'operands', или это может быть другой массив 'rtx' который Вы объявляете локально и инициализируете самостоятельно. Когда образец insn имеет несколько вариантов ограничений, часто появление ассемблерного кода определяется в основном тем, какой из вариантов был согласован. Когда это - так, C код может проверять переменную согласован первым (0 для первого, 1 для второго варианта, и т.д.). Например, предположим, что имеются два кода операции для сохранения нуля, как лбразец может использовать 'which_alternative ' для того, чтобы выбрать между ними: (define_insn "" [(set (match_operand:SI 0 "general_operand" "=r,m") (const_int 0))] "" "* 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.6. Ограничения операндов Каждый 'match_operand' в образце команды может определять ограничение на тип операндов. Ограничения могут указывать, может ли операнд быть в регистре, и в каких видах регистров; может ли операнд быть ячейкой памяти, и каких видов адрес; может ли операнд быть непосредственной константой, и какие возможные значения он может иметь. Ограничения могут также требовать соответствия между двумя операндами. 5.6.1. Простые Ограничения Самый простой вид ограничения - строка, заполненная символами, каждый из которых описывает один вид разрешенного операнда. Имеются следующие разрешенные символы: `m' Разрешается операнд памяти с любым видом адреса, который машина вообще поддерживает. `o' Разрешается операнд памяти только, если адрес - "смещаемый". Это означает, что добавление небольшого целого числа (фактически, ширины операнда в байтах, как определено его типом) может быть добавлено к адресу, и результат - также корректный адрес памяти. Например, адрес, который является постоянным - смещаемый; поэтому адрес, который является суммой регистра и константы тоже смещаемый (если только небольшое увеличение константы оставляет адрес внутри диапазона, поддерживаемого машиной); но самоувеличивающийся или самоуменьшающийся адрес - не смещаемый. Более сложные косвенные/индексированные адреса могут быть или не быть смещаемыми в зависимости от других способов адресации, которые поддерживает машина. Обратите внимание на то, что в операнде вывода, который может быть согласован с другим операндом, символ ограничения 'o' корректен только когда сопровождает оба операнда : '<' (если целевая машина имеет увеличение адреса) и '>' (если целевая машина имеет уменьшение адреса). `V' Не смещаемый операнд памяти. Другими словами, любой операнд, удовлетворяющий 'm' ограничению, но не 'o' ограничению. `<' Разрешаются операнды памяти с автоматической декрементацией адреса (либо преддекрементацией либо последекрементацией). `>' Разрешаются операнды памяти с автоматической инкрементацией адреса (либо прединкрементацией либо постинкрементацией). `r' Разрешается операнд регистра, при условии, что он находится в общем регистре. `d', `a', `f', ... Другие символы могут быть определены машинно-зависимым способом вместо конкретных классов регистров. 'D' , 'a' и 'f' определены на 68000/68020, для регистров данных, регистров адреса и регистров с плавающей точкой соответственно. `i' Разрешается непосредственный целочисленный операнд (с постоянным значением). Сюда так же включаются символьные константы, чьи значения будут известны только во время трансляции. `n' Разрешается непосредственный целочисленный операнд с известным числовым значением . Большинство систем не поддерживают константы, созданные во время трансляции, для операндов, меньших чем слово. Ограничения для этих операндов должны использовать скорее 'n' чем 'i' . `I', `J', `K', ... `P' Другие символы в диапазоне от 'I' до 'P' могут быть определены машинно-зависимым способом для разрешения непосредственных целочисленных операндов с явными целочисленными значениями в определенных диапазонах. Например, на 68000, 'I' определен для диапазона значений от 1 до 8. Это диапазон, разрешенный для размера сдвига в командах сдвига. `E' Разрешен непосредственный операнд с плавающей точкой (код выражения на целевой машине совпадает с соответствующим форматом на главной машине. `F' Разрешается непосредственный операнд с плавающей точкой (код выражения `G', `H' разрешать непосредственные вещественные операнды в конкретных диапазонах значений. `s' Разрешается непосредственный целочисленный операнд, чье значение не является явно заданным целым число. Это может показаться странным; если insn разрешает постоянный операнд со значением, не известным во времени компиляции, то конечно же он должен позволять и любое известное значение. Так почему используется 's' вместо Например, на 68000 в fullword команде возможно использовать непосредственный операнд; но если непосредственное значение - между -128 и 127, лучший код получится, если загрузить значение в регистр и использовать регистр. Это происходит из-за того, что загрузка в регистр может быть выполнена с помощью команды 'moveq'. Мы предусмотрели это, определив, что символ 'K' означает любое целое число вне диапазона от -128 до 127 и тогда записав 'Ks' в ограничения операнда. `g' Разрешается любой регистр, память или непосредственный целочисленный операнд, кроме регистров, которые не являются общими. `X' Разрешается любой операнд, даже если он не удовлетворяет когда некоторые варианты фактически не будут требовать рабочего регистра. `0', `1', `2', ... `9' Разрешается операнд, который соответствует определенному номеру операнда. Если цифра используется вместе с символами внутри того же самого варианта, то цифра должна стоять последней. Это называется 'соответствие ограничения' и это действительно означает то, что ассемблер имеет только один операнд, который выполняет две роли, рассматриваемые отдельными в RTL insn. Например, insn сложения имеет два входных операнда и один операнд вывода в RTL, но на большинстве CISC машин команда сложения в действительности имеет только два операнда, один из них операнд ввода-вывода: addl #35,r12 В этих обстоятельствах используется соответствующие ограничения . Более точно, два операнда, соответствующих друг другу, должны включить один только входной операнд и один только выходной операнд. Более того, цифра должна быть меньше, чем номер операнда, который использует ее в ограничении. То, что операнды соответствуют друг другу, в конкретном случае означает, что они идентично выглядящие выражения RTL. Но в нескольких специальных случаях разрешаются определенные виды различий. Например, вывода. Для правильных результатов в таких случаях, шаблон вывода должен всегда использовать номер операнда вывода при печати операнда. `p' Разрешается операнд, который является корректным адресом памяти. Это нужно для инструкций "load address" и "push address". корректным. `Q', `R', `S', ... `U' Символы в диапазоне от 'Q' до 'U' могут быть определены машинно-зависимым способом вместо произвольных типов операнда. Машинной макрокоманде описания 'EXTRA_CONSTRAINT' передается операнд как первый параметр и символ ограничения как второй операнд. Типичное использование для этого - различение некоторых типы ссылок памяти, которые воздействуют на другие insn операнды. Не определяйте эти символы ограничения, чтобы принять ссылки регистра ('reg'); проход перезагрузки не ожидает этого и не обработает это правильно. Чтобы иметь корректный код ассемблера, каждый операнд должен удовлетворить своим ограничениям. Но даже если это не так, то это не предотвращает действия образца. Взамен, он указывает транслятору изменить код так, чтобы ограничение было удовлетворено. Обычно это выполняется копированием операнда в регистр. Сравните два следующих образца: (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 не содержит два идентичных подвыражения в нужном месте. Образец сказал бы "Это не похоже на команду сложения; попробуйте другие образцы." Второй образец сказал бы "Да, это команда сложения, но с ней что-то не так." Он направил бы проход перезагрузки транслятора, чтобы сгенерировать дополнительные 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' не пропустится; если предикат более выборочный, то ограничения могут также быть более выборочные. ╥ Любое выражение операнда может быть перезаряжено, с помощью копирования в регистр. Так, если ограничения операнда позволяют некоторый регистр, то это заведомо безопасно. Требуется разрешить все классы регистраторов; транслятор знает, как копировать регистр в другой регистр соответствующего класса, чтобы делать корректные команды. ╥ Несмещаемая ячейка памяти может быть перезаряжена, копируя адрес в регистр. Так, если ограничение использует символ 'o', то все ячейки будут перезаряжены. ╥ Постоянный операнд может быть перезагружен с помощью выделения места в памяти, чтобы держать его как преинициализированные данные. Поэтому вместо константы может быть использована ссылка на память. Так, что если ограничения содержит символ `o' или `m', то константные операнды так же проходят. ╥ Если ограничение разрешает константу, и псевдорегистр, используемый в insn, который может быть расположен не в аппаратном регистре и поэтому эквивалентен константе, то регистр будет заменен на константу. Если предикат не разрешает константу, а insn по некоторым причинам повторно распознан, транслятор разрушится. Таким образом предикат должен всегда распознавать любые объекты, позволенные ограничением. Если предикат операнда пропускает регистры, а ограничение не разрешает их, то это может привести к аварийному отказу транслятора. Когда этот операнд регистр, проход перезагрузки будет stymied, потому что он не знает, как временно копировать регистр в память. 5.6.2. Многократные Альтернативные Ограничения Иногда одиночная команда имеет много альтернативных наборов возможных операндов. Например, на 68000, команда логического or может объединять регистр и непосредственное значение памяти, так же она может объединять операнд любого вида в регистр; но она не может объединять одну ячейку памяти с другой. Эти ограничения представляются несколькими вариантами. Вариант может быть описан рядом символов для каждого операнда. Полное ограничение для операнда сделано из символов для этого операнда из первого варианта, запятой, символы для этого операнда из варианта второго, запятой, и так далее до последнего варианта. Далее показывается как это делается для логического или с полными словами на 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, обращаются ко всем вариантам; их значение объясняется в следующем разделе (см. Предпочтения классов). Если все операнды удовлетворяют любому варианту, команда корректна. Иначе, для каждого варианта, транслятор рассчитывает, сколько команд должны быть добавлены, чтобы скопировать операнды таким образом, чтобы применился этот вариант. Будет выбран вариант, требующий наименьшего копирования. Если двум вариантам требуется одинаковое количество копирования, будет выбран первый. Эти выборы могут быть изменены с помощью символов '?'и '!': `?' Немного понижает приоритет варианта, в котором появляется. Работает в случае, когда не один из вариантов точно не выбран. Транслятор расценивает такой вариант как более приоритетный. `!' Понижает приоритет этого варианта до минимума. Транслятор все еще может использовать этот вариант, если он может подойти без перезагрузки. Но если перезагрузка необходима, то обязательно будет выбран другой вариант. Если образец insn имеет множество вариантов ограничений, то часто появление ассемблерного кода в основном определяется тем, какой вариант был выбран. Когда это - так, C код для записи кода ассемблера может использовать переменную 'which_alternative' , которой является порядковым номером варианта, который был фактически удовлетворен (0 для первого, 1 для варианта второго, и т.д.). см. Оператор вывода 5.6.3. Предпочтения классов регистров Ограничения операнда служат для других целей: они дают возможность транслятору решить, как лучше всего использовать аппаратные и псевдо регистры. Транслятор исследует ограничения, которые обращаются к insns, использующие псевдо регистр, ища машинно-зависимые символы типа 'd' и 'a' которые определяют классы регистраторов. Псевдо регистр помещен в любой класс становит наиболее голосующим. Символы ограничения 'g' и 'r' также голосуют: они голосуют в пользу общего регистра. В машинном описании говорится, какие регистры являются общими. Конечно, на некоторых машинах все регистры эквивалентны, и нет никаких классов регистров. И тогда все что описано ниже не нужно. 5.6.4. Символы модификатора ограничения Далее идут символы модификатора ограничения. `=' Означает, что этот операнд в этой команде только для чтения: предыдущее значение отбрасывается и заменяется данными результата. `+' Означает, что этот операнд инструкция может как читать так и писать. Когда транслятор устанавливает операнды, чтобы удовлетворить ограничению, требуется знать, какие операнды являются вводами к команде а какие являются выводами из нее. '=' идентифицирует вывод; '+' идентифицирует операнд, который является, и вводом и выводом; все другие операнды только входные. `&' Означает (в отличии от других случаев) что этот операнд записан прежде, чем команда завершена, используя входные операнды. Следовательно, этот операнд не может находиться в регистре, используемом как входной операнд или как часть любого адреса памяти. ограничениях с многократными вариантами, иногда один вариант требует '&' в то время как другие нет. См. например, '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 имеет команду для знакового распространения половины слова в регистр данных, и возможно также знаковое распространение с помощью копирования в регистр адреса. В то время как любой вид регистра приемлем, ограничения на адресата регистра адреса менее строги, так что самое лучшее, если распределение регистра заставляет адрес регистрировать цель. Следовательно, '*' используется так, чтобы символ ограничения '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.6.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' Непосредственные постоянные ограничения, для всех констант с плавающей точкой и для констант больших чем точность размера слова (обычно верхний регистр). `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' Целое число, которое удовлетворило бы ограничению 'I' если поменять у него знак (двойное дополнение) 'M' Целое число в диапазоне 0 к 32 'Q' Ячейка памяти, где точный адрес находится в одном регистре ( ``m'' является предпочтительной для `asm'-операнда ) 'R' Элемент constant pool 'S' Символ в текстовом сегменте текущего(актуального) файла *AMD 29000 family--`a29k.h'* 'l' Локальный регистр 0 'B' Регистр указателя байта ('BP') 'q' 'H' Специальный регистр цели `A' Первый регистр сумматор `a' Другой сумматор 'f' регистр с плавающей точкой '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' регистр с плавающей точкой 'H' 'Q' 'C' 'L' 'X' 'Y' 'I' 16-битная константа со знаком 'J' Константа, чьи первые 16 бит - 0 'K' Константа, чьи верхние 16 бит - 0 'L' Константа подходящий как операнд маски 'M' Константа больше чем 31 'N' Точная мощность 2 'O' Ноль 'P' Константа, у которой если поменять знак, то получится 16 битная знаковая константа 'G' Константа с плавающей точкой, которая может быть загружена в регистр с помощи одной команды на слово 'Q' Операнд Памяти, который является смещением из регистра ( 'm' является предпочтительным для `asm'-операторов ) *Intel 386--`i386.h'* 'q' 'A' 'f' регистр с плавающей точкой 'T' Первый регистр с плавающей точкой (начало стека) 'U' Второй регистр с плавающей точкой `a' `a' регистр `b' `b' регистр `c' `c' регистр `d' `d' регистр `D' `di' регистр `S' `si' регистр 'I' Константа в диапазоне 0 к 31 (для 32 разрядных сдвигов) 'J' Константа в диапазоне 0 к 63 (для 64 разрядных сдвигов) 'K' 'L' 'M' 0, 1, 2, или 3 (сдвигается c помощью 'lea' команды) 'G' Стандартная 80387 константа с плавающей точкой *Intel 960--`i960.h'* 'f' регистр с плавающей точкой ( ' fp0 ' к ' fp3 ' ) 'L' Локальный регистр ( ' r0 ' к ' r15 ' ) 'b' Глобальный регистр ( ' g0 ' к ' g15 ' ) 'd' Любой - локальный или глобальный регистр 'I' Целые числа от 0 до 31 'J' 0 'K' Целые числа от -31 до 0 'G' вещественный (с плавающей точкой) 0 'H' вещественный (с плавающей точкой) 1 *MIPS--`mips.h'* 'D' Целочисленный регистр общего назначения 'F' регистр с плавающей точкой (если доступен) 'H' 'L' 'X' 'Y' Целочисленный регистр общего назначения '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' Ячейка памяти, которая может быть загружена одной командой ( '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 константой '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' Просто регистр 5.6.6. Без ограничений Некоторые машины настолько чисты, что ограничения на операнды просто не нужны. Например, на Vax операнд, корректный в одном контексте, будет так же корректен и в любом другом контексте. На таких машинах каждый операнд должен иметь ограничение `g', исключая, разве что, операнды инструкций загрузки адреса, которые написаны, как будто они обращаются к ячейкам памяти, но фактический они обращаются к адресу ячейки. Они имели бы ограничение 'p'. Для таких машин, вместо того, чтобы записывать 'g' и 'p' для всех ограничений, Вы можете писать описание с пустыми ограничениями. Тогда Вы пишете `""' для ограничения в каждом 'match_operand' . Операнды Адреса идентифицируются, записыванием выражения `address' рядом с Когда машинное описание имеет только пустые ограничения, некоторые части трансляции будут пропущены, что делает транслятор быстрым. Однако, только очень малое число машин фактически не нуждаются в ограничениях; все машинные описания теперь уже включают ограничения использования. 5.7. Стандартные Образцы Имен Для Генерации Вот таблица имен команд, которые являются значимыми в проход генерации RTL транслятора. Присваивание одного из этих имен образцу команды сообщает проходу генерации RTL, что он может использовать этот образец для выполнения некоторой задачи. Здесь M - имя машинного типа из двух символов, строчными буквами. Этот образец команды перемещает данные с этим машинным типом из операнда 1 в операнд 0. Например, " movsi " перемещает данные длиной в слово. Если операнд 0 - " subreg " с типом M регистра, чей собственный тип более широкий, чем M, эффект этой команды состоит в сохранении указанного значения в части регистра, соответствующей типу M. Эффект на остальной части регистра не определен. Этот класс образцов выделяется по нескольким причинам. Прежде всего каждое из этих имен *должно* быть определено, потому что нет никакого другого способа скопировать данные из одного места в другое. Во-вторых, эти образцы используются не только в проходе генерации RTL. Даже проход перезагрузки может генерировать перемещение insns, чтобы копировать значения из слотов стека во временные регистры. Когда он делает это, один из операндов - аппаратный регистр, а другой - операнд, который может потребоваться перезагрузить в регистр. Следовательно, когда задана такая пара операндов, образец должен генерировать RTL, который не нуждается в перезагрузке и не нуждается во временных регистрах - никаких регистрах, отличных от операндов. Например, если Вы поддерживаете образец с " define_expand ", то в таком случае " define_expand " не должен вызывать " force_reg " или любую другую такую функцию, которая могла бы генерировать новые псевдорегистры. Это требование существует даже для типов подслова на RISC машине, на которой при выборке этих типов из памяти обычно требуется несколько insns и несколько временные регистров. Посмотрите в " spur.md ", чтобы увидеть как это требование может быть удовлетворено. В течение перезагрузки ссылка памяти с недопустимым адресом может быть передана как операнд. Такой адрес будет заменен на допустимый адрес позже в проходе перезагрузки. В этом случае с адресом нельзя делать ничего, за исключением использования его так, как это имеет место. Если он копируется, он не заменяется на имеющий силу адрес. Не должно делаться никакой попытки сделать такой адрес допустимым и нельзя вызывать подпрограмму (типа " change_address "), которая будет это делать. Обратите внимание, что " general_operand " потерпит неудачу, если применить его к такому адресу. Глобальная переменная " reload_in_progress " (которая должна быть явно объявлена, если требуется) может использоваться, чтобы определить, требуется ли такая специальная обработка. Виды операндов, которые могут перезагружаться, зависят от остальной части машинного описания, но обычно на 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 " с фиксированной точкой должны быть предназначены, чтобы избежать попытки перезагрузки в регистр с плавающей точкой. Подобно " movM ", но используется, когда рабочий регистр требуется, чтобы передвигать между операндом 0 и 1 операндом. Операнд 2 описывает рабочий регистр. См. обсуждение макрокоманды " SECONDARY_RELOAD_CLASs " в * См.: Классы Регистров:: .. Подобно " movM " за исключением того, что, если операнд 0 - " subreg " с типом M регистра, чей естественный тип более широкий, " movstrictM " команда гарантированно не изменит регистра за исключением части, которая принадлежит типу M. Загружает несколько последовательных ячеек памяти в последовательные регистры. Операнд 0 - первый из последовательных регистров, операнд 1 - первая ячейка памяти, а операнд 2 - константа: число последовательных регистров. Определяйте эту команду, только если целевая машина действительно имеет такую команду; не определяйте ее, если наиболее эффективный путь загрузки последовательных регистров из памяти - загружать их по одному. На некоторых машинах имеются ограничения на то, какая именно последовательность регистров может быть сохранена в память, типа ограничений на начальные или конечные номера регистров или на диапазон допустимых количеств. Для таких машин используйте " define_expand " (* См.: Расширитель определений::.) и делайте сбой образца, если ограничения не выполнены. Записывайте сгенерированный insn как " parallel " с элементами, являющимися " set " одного регистра из соответствующей ячейки памяти (Вам могут также потребоваться " use " или " clobber " элементы). Используйте " match_parallel ", (* См.: RTL Шаблон::.) чтобы распознать insn. См. " a29k.md " и " rs6000.md " для примеров использования этого образца insn. Подобно " load_multiple ", но сохраняет несколько последовательных регистров в последовательные ячейки памяти. Операнд 0 - первая из последовательных ячейки памяти, операнд 1 - первый регистр, а операнд 2 - константа: число последовательных регистров. Добавляет операнд 2 и 1 операнд, сохраняя результат в операнде 0. Все операнды должны иметь тип M. Это может использоваться даже на двухадресных машинах при помощи ограничений, требующих, чтобы операнды 1 и 0 находились в одном и том же месте. Аналогично, но для других арифметических операций. Перемножает операнды 1 и 2, которые имеют тип " HImode ", и сохраняет " SImode " результат в операнде 0. Аналогичные команды умножения с расширением для другой ширины. Аналогичные команды умножения с расширением, но для беззнакового умножения . Выполняет знаковое умножение операндов 1 и 2, которые имеют Режим M, и сохраняют старшую половину результата в операнд 0. Младшая половина результата теряется. Аналогичная команда для беззнакового умножения. Знаковое деление, дающее и частное, и остаток. Операнд 1 делится на операнд 2, частное сохраняется в операнде 0, а остаток сохраняется в операнде 3. Для машин с командой, которая выдает и частное, и остаток, сделайте образец для " divmodM4 " но не делайте образцов для " divM3 " и " modM3 ". Это позволяет оптимизацию в относительно общем случае, когда и частное, и остаток вычисляются. Если команда, которая вычисляет только частное или только остаток, существует и более эффективна, чем команда, которая вычисляет и то, и другое, пишите программу вывода " divmodM4 " для вызова " find_reg_note " и поиска примечания " REG_UNUSED " для частного или остатка и генерации соответствующей команды. Аналогично, но делает деление без знака. Арифметический сдвиг операнда 1 влево на число битов, задаваемое операндом 2, с сохранением результата в операнде 0. Здесь M - тип операнда 0 и операнда 1; тип операнда 2 определяется образцом команды, и транслятор преобразует операнд к этому типу перед генерацией команды. Другие команды сдвига и вращения , аналогичные " ashlM3 ". Получает число, противоположного операнду 1, и записывает результата в операнд 0. Записывает абсолютное значение операнда 1 в операнд 0. Записывает квадратный корень из операнда 1 в операнд 0. Встроенная функция C " sqrt " всегда использует тип, который соответствует типу данных C " double ". Записывает в операнд 0 единицу плюс номер самого младшего из установленных в 1 битов 1 операнда. Если операнд 1 - ноль, записывается ноль. M - тип операнда 0; операнда 1 тип определяется образцом команды, и транслятор преобразует операнд к этому типу перед генерацией команды. Встроенная функция C " ffs " всегда использует тип, который соответствует типу данных C " int ". Записывает поразрядное дополнение операнда 1 в операнд 0. Сравнивает операнд 0 и операнд 1 и устанавливает условные коды. RTL образец должен выглядеть следующим образом: (set (cc0) (compare (match_operand:M 0 ...) (Match_operand:M 1 ...))) Сравнивает операнд 0 c нулем и устанавливает условные коды. RTL образец должен выглядеть следующим образом: (set (cc0) (match_operand:M 0 ...)) Образцы " tstM " не должны определяться для машин, которые не используют " (cc0) ", поскольку это помешало бы оптимизации, так как перестало бы быть понятным, какие операции " set " являются сравнениями. Вместо них должны использоваться образцы " cmpM ". Команда перемещения блока. Адреса исходной строки и строки-результата - первые два операнда, и они оба имеют тип " Pmode ". Число перемещаемых байтов - третий операнд, в типе M. Четвертый операнд - известное общее выравнивание исходной строки и строки-результата, в форме " const_int " rtx. Таким образом, если транслятор знает, что обе строки выравниваются на границу слова, он может задавать значение 4 для этого операнда. Этим образцам не требуется специальное рассмотрение возможности того, что источник и результат могут перекрываться. Команда сравнения блоков, с пятью операндами. Операнд 0 - результат; он имеет тип M. Остальные четыре операнда соответствуют операндам " movstrM ". Два указанных блока памяти сравниваются побайтно в лексикографическом порядке. Результатом команда является запись значения в операнд 0, знак которого указывает результат сравнения. Вычисление длины строки, с тремя операндами. Операнд 0 - результат (типа M), операнд 1 - ссылка " mem " на первый символ строки, операнд 2 - символ для поиска (обычно ноль) и операнд 3 - константа, описывающая известное выравнивание начала строки. Преобразует целочисленный знаковый операнд 1 (допустимый для типа с фиксированной точкой M) к типу с плавающей точкой N и записывает результат в операнд 0 (который имеет тип N). Преобразует целочисленный беззнаковый операнд 1 (допустимый для типа с фиксированной точкой M) к типу с плавающей точкой N и записывает результат в операнд 0 (который имеет тип N). Преобразует операнд 1 (допустимый для типа с плавающей точкой M) к типу с фиксированной точкой N как число со знаком и записывает результат в операнд 0 (который имеет тип N). Результат этой команды определен только тогда, когда значение операнда 1 - целое число. Преобразует операнд 1 (допустимый для типа с плавающей точкой M) к типу с фиксированной точкой N как число без знака и записывает результат в операнд 0 (который имеет тип N). Результат этой команды определен только тогда, когда значение операнда 1 - целое число. Преобразует операнд 1 (допустимый для типа с плавающей точкой M) к целому числу, по-прежнему представляемому в типе с плавающей точкой M, и записывает результат в операнд 0 (который имеет тип M). Подобно " fixMN2 ", но преобразует значение с плавающей точкой типа M к целому числу. Подобно " fixunsMN2 ", но преобразует значение с плавающей точкой типа M к целому числу. Усекает операнд 1 (допустимый для типа M) к типу N и записывает в операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной точкой или оба с плавающей точкой. Расширяет знаком операнд 1 (допустимый для типа M) к типу N и записывает в операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной точкой или оба с плавающей точкой. Расширяет нулем операнд 1 (допустимый для типа M) к типу N и записывает в операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной точкой. Извлечекает поле битов из операнда 1 (регистра или ячейки памяти), где операнд 2 определяет ширину в битах, а операнд 3 - начальный бит, и записывает его в операнд 0. Операнд 0 должен иметь тип " word_mode ". Операнд 1 может иметь тип " byte_mode " или " word_mode "; часто " word_mode " позволяется только для регистров. Операнды 2 и 3 должны быть допустимы для " word_mode ". Проход генерации RTL генерирует эту команду только с константами в качестве операндов 2 и 3. Значение битового поля расширяется знаком до полного целочисленного слова перед записью в операнд 0. Подобно " extv ", за исключением того, что значение битового поля расширяется нулем. Записывает операнд 3 (который должен быть допустимым для " word_mode ") в битовое поле в операнде 0, где операнд 1 определяет ширину в битах, а операнд 2 - начальный бит. Операнд 0 может иметь тип " byte_mode " или " word_mode "; часто " word_mode " позволяется только для регистров. Операнды, 1 и 2 должны быть допустимыми для " word_mode ". Проход генерации RTL генерирует эту команду только с константами в качестве операндов 1 и 2. Перемещает операнд 2 или операнд 3 в операнд 0 согласно сравнению в операнде 1. Если сравнение истинно, операнд 2 перемещается в операнд 0, иначе перемещается операнд 3. Режим сравниваемых операндов не обязан быть таким же, как у перемещаемых операнды. Некоторые машины, например sparc64, имеют команды, которые перемещают целочисленное значение в зависимости от сравнения чисел с плавающей точкой и наоборот. Если машина не имеет условных команд перемещения, не определяйте эти образцы. Записывает ноль или отличное от нуля число в операнд согласно коду условия. Записываемое значение отлично от нуля, если условие COND истинно. COND - имя кода выражения операции сравнения, такого как " eq ", " lt " или " leu ". Вы указываете тип, который операнд должен иметь, когда Вы пишете выражение " match_operand ". Транслятор автоматически видит, какой тип Вы использовали и поддерживает операнд этого типа. Значение, сохраненное для истинного условия, должно иметь 1 в младшем бите или должно быть отрицательно. Иначе команда не подходит и Вы должны исключить ее из машинного описания. Вы описываете транслятору точно, какое значение записывается, определяя макрокоманда " STORE_FLAG_VALUE " (* См.: Разное::.). Если описание не найдено, что может использоваться для всех образцов " sCOND ", Вы должны исключить эти операции из машинного описания. Эти операции могут не работать, но так должно быть только в относительно нестандартных случаях; если они не будут работать для обычных случаев, включая целочисленные сравнения, лучше всего исключить эти образцы. Если эти операции исключены, транслятор будет обычно генерировать код, который копирует константу 1 в адресат и делает условный переход вокруг присваивания нуля адресату. Если этот код более эффективен, чем потенциальные команды, используемые для образца " sCOND ", сопровождаемые теми, которые требуются, чтобы преобразовать результат в 1 или ноль в " SImode ", Вы должны исключить " sCOND " операции из машинного описания. Команда условного перехода. Операнд 0 - " label_ref ", ссылающийся на метку, на которую делается переход. Переход происходит, если коды условия удовлетворяют условию COND. Некоторые машины не следуют модели, принятой здесь, в которой команда сравнения сопровождается командой условного перехода. В этом случае образцы " cmpM " (и " tstM ") должны просто сохранять куда-нибудь операнды и генерировать все необходимые insns в " define_expand " (* См.: Расширение определений::.) для операций условного перехода(выполняет перехода). Всем обращениям к расширению образцов " bCOND " должны непосредственно предшествовать обращения к расширению образцов " cmpM " или " tstM ". Машины, которые используют псевдорегистр для значения кода условия, или где тип, используемый для сравнения, зависит от проверяемого условия, должен также использовать вышеупомянутый механизм. * См.: Образцы Переходов:: Вышесказанное применимо также к образцам " movMODEcc " и " sCOND ". Команда вызова подпрограммы, не возвращающая значения. Операнд 0 - вызываемая функция; операнд 1 - число байтов параметров, помещенных в стек (в типе " SImode ", если это не " const_int "); Операнд 2 - число регистров, используемых как операнды. На большинстве машин операнд 2 фактически не сохранен в образце RTL. Это делается ради некоторых RISC машин, которые должны поместить эту информацию в код ассемблера; они могут помещать ее в RTL вместо операнда 1. Операнд 0 должен быть " mem " RTX, чей адрес - адрес функции. Обратите внимание, однако, что этот адрес может быть " symbol_ref " выражением, даже если он не был бы законным адресом памяти на целевой машине. Если это также недопустимый параметр для команды вызова, образцом для этой операции должен быть " define_expand " (* См.: Расширение определений::.), который помещает адрес в регистр и использует этот регистр в команде вызова. Команда вызова подпрограммы, возвращающая значение. Операнд 0 - аппаратный регистр, в котором значение возвращается. Имеется еще три операнда, такие же, как три операнда команды " call " (но с номерами, увеличенными на один). Подпрограммы, которые возвращают объекты ` BLKmode ', используют " call " insn. Аналогичны " call " и " call_value ", за исключением того, что используются, если определены и если " RETURN_POPS_ARGS " отлично от нуля. Они должны сгенерировать " parallel ", который содержит и обращение к функции, и " set ", чтобы указать корректировку, делаемую для указателя кадра. Для машин, где " RETURN_POPS_ARGS " может быть отлично от нуля, использование этих образцов увеличивает число функций, для которых указатель кадра может быть удален, если это желательно. Команда вызова подпрограммы, возвращающая значение любого типа. Операнд 0 - вызываемая функция; операнд 1 - ячейка памяти, где должен быть сохранен результат вызова функции; операнд 2 - выражение ` parallel ', где каждый элемент - выражение " set ", которое указывает запись возвращаемого функцией значения в блок результата. Этот образец команды должен быть определен, чтобы поддерживать " __ builtin_apply " на машинах, где для вызова подпрограммы с произвольными параметрами или сохранения возвращенного значения необходимы специальные команды. Этот образец команды требуется на машинах, имеющих несколько регистров, которые могут содержать возвращаемое значение (то есть " FUNCTION_VALUE_REGNO_P " является истиной для более, чем одного регистра). Команда возврата из подпрограммы. Это имя образца команды должно быть определено только, если одиночная команда может выполнить всю работу по возврату из функции. Подобно образцам " movM ", этот образец также используется после фазы генерации RTL. В этом случае он должен поддерживать машины, где для возврата из функции обычно необходимо несколько команд, но некоторый класс функций требует только одну команду, чтобы выполнить возврат. Обычно соответствующие функции - это те, которые не должны сохранять никаких регистров и не должны распределять место в стеке. Для таких машин, условие, определенное в этом образце должно быть истинным только, когда " 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 ". Команда возврата из подпрограммы без контроля типов . Этот образец команды должен быть определен для поддержки " __ builtin_return " на машинах, где необходимы специальные команды, чтобы возвратить значение любого типа. Операнд 0 - ячейка памяти, куда записывается результат вызова функции с " __ builtin_apply "; операнд 1 - выражение ` parallel ', где каждый элемент - выражение ` set ', указывающее восстановление значения, возвращаемого функцией из блока результата. Пустая команда. Это имя образца команды должно быть всегда определено, чтобы вывести пустую команду в коде ассемблера. " (const_int 0) " будет делать это как образец RTL. Команда для перехода к адресу, который является нулевым операндом. Это имя образца обязательно на всех машинах. Команда для перехода через таблицу, включая проверку границ. Эта команда имеет пять операндов: 1. Индекс для посылки, который имеет тип " SImode ". 2. Нижняя граница индексов в таблице, целочисленная константа. 3. Общий диапазон индексов в таблице - самый большой индекс минус самый маленький (включая оба конца). 4. Метка, которая непосредственно предшествует таблице. 5. Метка, на которую делается переход, если индекс имеет значение вне границ. (Если макрокоманда с машинным описанием " CASE_DROPS_THROUGH " определена, то такой индекс влечет переход к коду после таблицы переходов вместо перехода к этой метке. В таком случае эта метка в действительности не используется командой " casesi ", но она всегда задается как операнд.) Таблица - это " addr_vec " или " addr_diff_vec " внутри " jump_insn ". Число элементов в таблице - один плюс разность между верхней и нижней границами. Команда для перехода к переменному адресу. Это - низкоуровневая возможность, которая может использоваться, чтобы выполнить таблицу переходов, когда нет образца " casesi ". Этому образцу требуется два операнда: адрес или смещение, и метка, которая должна непосредственно предшествовать таблице перехода. Если макрокоманда " CASE_VECTOR_PC_RELATIVE " определена, то первый операнд является смещением, которое рассчитывается из адреса таблицы; иначе, это - абсолютный адрес перехода. В любом случае первый операнд имеет тип " Pmode ". " tablejump " insn - всегда последний insn перед таблицей перехода, которую он использует. Код ассемблера обычно не имеет потребности в использовании второго операнда, но Вы должны соединить их в образце RTL так, что оптимизатор перехода не будет удалять таблицу как недостижимый код. Большинство машин сохраняет и восстанавливает указатель стека, копируя его в или из объекта типа " Pmode ". Не определяйте эти образцы на таких машинах. Некоторые машины требуют специальной обработки для сохранения и восстановления указателя стека. На этих машинах определяйте образцы соответственно нестандартным случаям, используя " define_expand " (* См.: Расширение Определений::.), который производит требуемый insns. Вот три типа сохранения и восстановления: 1. " save_stack_block " сохраняет указатель стека в начале блока, который распределяет объект переменного размера, а " restore_stack_block " восстанавливает указатель стека, когда блок покидается. 2. " save_stack_function " и " restore_stack_function " делают аналогичную работу для самого внешнего блока функции и используются, когда функция распределяет объекты переменного размера или вызывает " alloca ". Только эпилог использует восстановленный указатель стека, позволяя более простую последовательность сохранения и восстанавления на некоторых машинах. 3. " save_stack_nonlocal " используется в функциях, которые содержат метки для перехода к вложенными функциями. Он сохраняет указатель стека таким способом, что внутренняя функция может использовать " restore_stack_nonlocal ", чтобы восстановить указатель стека. Транслятор генерирует код, чтобы восстановить регистры указателей кадра и параметров, но некоторые машины требуют сохранения и восстановления дополнительных данных типа информации окна регистра или обратных цепочек стека. Поместите в эти образцы insns для сохранения и восстановления любых таких данных. При сохранении указателя стека операнд 0 - область сохранения и операнд 1 - указатель стека. Режим, используемый для распределения области сохранения - тип операнда 0. Вы должны указать целый тип или " VOIDmode ", если область сохранения не нужна в данном случае (потому что сохранение не требуется или потому что может использоваться сохранение в машинно-специфическую область сохранения). Операнд 0 - указатель стека, а операнд 1 - область сохранения для операций восстановления. Если " save_stack_block " определен, операнд 0 не должен быть " VOIDmode ", так как такие сохранения могут произвольно вкладываться. Область сохранения - " mem ", который является константным смещении от " virtual_stack_vars_rtx ", когда указатель стека сохраняется для использования нелокальных goto и " reg " в других двух случаях. Вычитает (или добавляет, если " STACK_GROWS_DOWNWARD " не определено) операнд 0 из указателя стека, чтобы создать пространство для динамически распределяемых данных. Не определяйт этот образец, если все, что должно выполняться - вычитание. Некоторые машины требуют других операций типа исследования стека или поддержания обратной цепочки. Определите этот образец, чтобы сгенерировать эти операции в дополнение к модифицированию указателя стека. 5.8. Когда Порядок Образцов Играет Роль Иногда insn может соответствовать более чем одному образцу команды. Тогда используется образец, который появляется первым в машинном описании. Следовательно, более специфические образцы (образцы, которые будут соответствовать меньшее количество insns) и более быстрые команды (которые производят лучший код, когда имеет место соответствие им) должны обычно идти сначала в описании. В некоторых случаях эффект упорядочивания образцов может использоваться, чтобы скрыть образец, когда он не является допустимым. Например, 68000 имеет команду для преобразования слова к плавающей точке и другую для преобразования байта к плавающей точке. Команда, преобразующая целое число к плавающей точке может соответствовать любой из них. Мы помещаем образец преобразования слова сначала, чтобы удостовериться, что он будет использоваться скорее, чем другой. (Иначе большое целое число могло бы быть сгенерировано как несколько одиночных байтов, что не работало бы.) Вместо использования такого упорядочивания образцов можно было бы сделать образец для convert-a- byte достаточно "умный", чтобы правильно обрабатывать любую целочисленную константу. 5.9. Взаимозависимость Образцов Каждое машинное описание должно иметь поименованный образец для каждого из имен условных переходов " 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 "" "")))) Они необходимы, потому что оптимизация перехода может превращать прямое ветвление в обратное. Часто удобно использовать конструкцию " 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 ", результаты могли бы быть неправильными, если константа фактически не подходит для этого типа. Такие команды расширения констант редко генерируются, потому что они оптимизируются, но они могут иногда встречаться при неоптимизированной трансляции. Если ограничение в образце позволяет константу, проход перезагрузки может в некоторых случаях заменять регистр на константу, разрешенную ограничением. Аналогично для ссылок памяти. Из-за этой замены Вы не должны обеспечивать отдельные образцы для команд инкремента и декремента. Вместо этого они будут генерироваться из одного и того же образца, который поддерживает insns сложения из регистра в регистр, при помощи рассмотрения операндов и генерации соответствующей машинной команды. 5.10. Определение Образцов Команд Перехода Для большинства машин 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 "; другими словами, нет излишних команд сравнения. Некоторые машины имеют команды одновременного сравнения и перехода и не имеют условного кода. Подобная методика работает для них. Когда наступает время, чтобы "вывести" команду сравнения, запишите ее операнды в двух статических переменных. Когда выводится следующая команда одновременного сравнения и перехода, фактически выводится команды одновременного сравнения и перехода, которая использует записанные операнды. Этот способ также работает при определении образцов для команды одновременного сравнения и перехода. При оптимизирующей трансляции пара команд сравнения и перехода будет объединена согласно этим образцам. Но этого не происходит, если оптимизация не запрошена. Так что Вы должны использовать одно из решений, указанных выше, в дополнение к любым специальным образцам, которые Вы определяете. На многих RISC машинах большинство команд не воздействует на код условия и там может даже не быть отдельного регистра кода условия. На этих машинах ограничение, что определение и использование кода условия должно производиться смежными insns, не необходимо и может помешать важным оптимизациям. Например, на IBM RS/6000, имеется задержка для принимаемых ветвей, если регистр кода условия не установлен на три команды раньше условного перехода. Планировщик команд не может выполнять эту оптимизацию, если не разрешается разделите определение и использование регистра кода условия. На этих машинах не используйте " (cc0) ", а используйте взамен регистр для представления кода условия. Если в машине имеется определенный регистр кода условия, используйте аппаратный регистр. Если код условия или результат сравнения может быть помещен в любой общий регистр, или если имеется несколько регистров условия, используйте псевдорегистр. На некоторых машинах тип сгенерированной команды перехода может зависеть от способа, которым код условия был произведен; например, на 68k и Sparc установка кода условия непосредственно из команд сложения или вычитания не очищает бит переполнения, в отличие от команды тестирования, так что для некоторых ветвлений должны использоваться различные команды перехода. Для машин, которые используют " (cc0) ", установка и использование кода условия должны быть смежными (разделяясь только " note " 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)))] "" "...") Макрокоманда " SELECT_CC_MODE " на Sparc возвращает " CC_NOOVmode " для сравнения, чей параметр - " plus ". 5.11. Канонизация Команд Часто встречаются случаи, когда несколько выражений 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)) ". ╥ Внутри вычислений адреса (то есть внутри " 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)) ". ╥ Сумма трех элементов, один из которых - константа, будет появляться только в форме (plus:M (plus:M X Y) CONSTANT) ╥ На машинах, которые не используют " cc0 ", " (compare X (const_int 0)) " будет преобразовано в X. ╥ Сравнения на равенство группы битов (обычно одиночного бита) с нулем будет записаны, используя " zero_extract " скорее, чем эквивалент операций " and " или " sign_extract ". 5.12. Машинно - специфические локальные оптимизации В дополнение к образцам команд " md " файл может содержать определения машинно - специфических peephole оптимизаций. Peephole оптимизация - это оптимизация " просмотром вперед ", т.е. поиск команд, которые можно объединить с данной для ускорения их выполнения. Комбинатор не обращает внимание на некоторые peephole оптимизации, когда поток данных в программе не предполагает, что он попробует сделать их. Например, иногда два последовательного insns, связанные по цели, могут быть oбъединены, даже если кажется, что второй не использует регистр, вычисленный в первом. Машинно - специфический peephole оптимизатор может обнаружить такие возможности. Определение выглядит примерно так: (define_peephole [INSN-PATTERN-1 INSN-PATTERN-2 ...] "CONDITION" "TEMPLATE" " OPTIONAL INSN-ATTRIBUTES ") Последний строковый операнд может быть опущен, если Вы не используете никакой машинно-специфической информации в этом машинном описании. Если он имеется, то он должен подчиняться тем же правилам,что и в " define_insn ". В этом скелете 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 определение может проверять, какие операнды заканчиваются в каких видах регистров, только просматривая операнды. Способ обратиться к операндам 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"))] "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,\", operands); return \"fmoved sp@+,%0\"; #endif } ") Эффект этой оптимизации заключается в изменении jbsr _foobar addql *4,sp movel d1, movel d0, fmoved sp@+, fp0 на jbsr _foobar movel d1,sp@ movel d0, fmoved sp@+,fp0 INSN-PATTERN-1 и так далее выглядят *почти* как второй операнд " define_insn ". Имеется одно важное различие: второй операнд " define_insn " состоит из одного или нескольких RTX, заключенных в квадратные скобки. Обычно, имеется только один: затем то же самое действие может быть записано как элемент " define_peephole ". Но когда в " define_insn " несколько действий, они неявно включены в " 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.13. Определенные RTL последовательности для генерации кода На некоторых целевых машинах отдельные стандартные имена образцов для RTL поколения не могут быть обработаны одиночным insn, но их можно представить последовательностью insns. Для этих целевых машин, Вы можете описать только для RTL поколения и он может произвести больше чем один RTL- insn. RTX 'define_expand' имеет четыре операнда: ╥ Имя. Каждый 'define_expand' должен иметь имя, так как обратится к нему можно только через имя. ╥ RTL шаблон. Точно так же как для RTL шаблона для 'define_peephole', это массив выражений RTL, каждый из которых - один insn. ╥ Условие - строка, содержащая выражение на С. Это выражение используется, что бы выразить, как доступность этого образца зависит от подклассов целевой машины, выбираемых с помощью командно-строчных опций, когда GNU CC запущен. Это похоже на условие 'define_insn', которое имеет стандартное имя. Таким образом, условие (если присутствует) может не зависеть от данных в согласовываемом insn, но только от флагов типа в целевой машине. Транслятор должен проверить эти условия во время инициализации, что бы узнать какие именно названные инструкции доступны в особенном запуске (particular run). ╥ Операторы подготовки - строка, содержащая ноль или больше С-утверждений, которые должны быть выполнены до того, как из RTL-шаблона будет сгенерирован RTL-код. Обычно эти операторы подготавливают временные регистры для использования в виде внутренних операндов, но они могут также непосредственно генерировать RTL insns, вызывая подпрограммы типа из RTL шаблона. Каждому RTL insn, сгенерированному `define_expand' должен соответствовать какой-нибудь `define_insn' в описании машины. Иначе транслятор разрушится при попытке сгенерировать соответствующий код, или при попытке его оптимизации. Так же как и операторы управления RTL insns, RTL шаблон описывает операнды, которые должны быть определены, при использовании образца. В частности, это дает предикат для каждого операнда. Правильный операнд, который должен быть указан при генерации RTL с образца, должен быть описан с помощью 'mathch_operand' в свое первое появления в RTL шаблоне. Это вводит информацию о предикате операнда в таблицы, в которые записываются такие вещи. GNU CC использует эту информацию для предзагрузки операнда в регистр, если это требуется, что бы сделать корректный RTL код. Если на операнд ссылаются более одного раза, то последующие ссылки должны использовать 'mathch_dup'. RTL-шаблон может также ссылаться на внутренние 'операнды', которые являются временными регистрами или метками, используемыми только в последовательности, сгенерированной 'define_expand'. Внутренние операнды в RTL шаблоне заменяются на 'match_dup', но никогда не на 'match_operand'. Значение внутренних операндов, когда требуется использование этого образца, не передаются транслятором как аргументы. Вместо этого они вычисляются в образце, в операторах подготовки. Эти операторы вычисляют значения и записывают их в соответствующие элементы 'операндов' так, чтобы Имеются две специальных макрокоманды, определенные для использования в операторах подготовки: `DONE' and `FAIL'. Если поставить после них ';' то можно использовать их как операторы. `DONE' Используйте макрокоманду `DONE', чтобы закончить для образца генерацию RTL. Единственными RTL insn, полученными из образца в этом случае будут те, которые уже сгенерированы явными обращениями к 'emit_insn' в операторах подготовки; RTL шаблон не будет сгенерирован. `FAIL' Заставляет образец обломиться. Когда образец обламывается, это означает, что образец не был по настоящему доступен. Тогда подпрограммы вызова в трансляторе пробует другие способы для генерации кода, используя другие образцы. Отказ в настоящее время поддерживается только для бинарных (сложение, умножение, смещение, и т.д.) и битовых (`extv', `extzv', и `insv') операций . Далее идет пример определения левого сдвига для чипа 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_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]);') Здесь сгенерированы два RTL insn - один что бы очистить весь выходной операнд, а другой для того, что бы копировать входной операнд в его нижнею половину. Эта последовательность некорректна если входной операнд ссылается на выходной операнд (на его старое значение), поэтому операторы подготовки проверяют, что это не так. Функция 'make_safe_from' копирует операнд Это делается с помощью генерации другого RTL insn. Наконец, третий пример демонстрирует использование внутреннего операнда Ноль-расширение на SPUR чипах осуществляется с помощью применения представлена с помощью '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 в сгенерированной последовательности не должен быть или `call_insn'. Если Вам не нужен настоящий insn в конце, то сгенерируйте insn, который копирует результат операций сам в себя. Такой insn не генерирует кода, но может предохранить от проблем в трансляторе. 5.14. Как разделять инструкции Существует два случая, когда Вы должны определить, как происходит расчленение команды на много insn. На машинах, имеющих команды, нуждающиеся в задержке (см. Слот задержки) или команды, вывод которых не доступен для многократных циклов. Фазы транслятора, которые оптимизируют эти случаи должны быть способна перемещать insn в одно-командный слот задержки. Однако, некоторые insns могут генерировать более одной машинной команды. Эти insn не могут быть помещены в слот задержки. Часто Вы можете заменить insn на список список insnов каждое из которых соответствует одной машинной команде. Недостаток - медленная трансляция, требующая большого количества памяти. Если возникающий в результате insns слишком сложен, то оптимизация в трансляции уменьшается. Если есть причины предположить, что разбиение insn улучшит команду или планирование слота задержки, то транслятор его выполняет. Фаза комбинирования insn также разбивает мнимые (putative) 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-PAT╜ TERN-2, и т.д. 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_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' и затем то же выражение 'set' внутри 'parallel', после которого идет клоббирование псевдо регистра, что бы использовать его как 'scratch' регистр. В этих случаях комбайнер точно знает, какие два новых insn-образца должны быть сгенерированы. Он проверит, соответствуют ли эти образцы каким-нибудь никогда не сгенерирует соответствующих 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 регистр, генерирует сравнение равенства регистра и большой константы: (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 ', так и несоответствующие. Вместо этого надо писать два отдельных 5.15. Атрибуты команд В дополнение к описанию команд, поддерживаемых целевой машиной, файл 'md' также определяет группу 'атрибутов' и набор значений для каждого из них. Каждому сгенерированному insn назначено значение для каждого атрибута. Возможно будет один атрибут, показывающий что insn обманывает (has on) машинный код условия. Этот атрибут 'NOTICE_UPDATE_CC' может затем использоваться для отслеживания кодов условий. 5.15.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' ...) следующие строки будут записаны в файл '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.15.2. Атрибут-выражения Выражения RTL, используемые, чтобы определить атрибуты, используют коды, описанные выше, плюс несколько специфических для областей определения атрибута кодов. Эти выражения будут обсуждены далее. Атрибут-выражения должны иметь одну из следующих форм: `(const_int I)' Целое число I определяет значение числового атрибута. I должно быть неотрицателеным. Значение числового атрибута может быть определено либо с помощью `const_string', `eq_attr' (см.ниже), и `set_attr'. `(const_string VALUE)' Строка VALUE определяет значение атрибутов константы. Если VALUE определено как `'*'', то это означает, что по умолчанию значение атрибута должно использоваться для insn, содержащего это выражение. `'*'' очевидно не может использоваться в DEFAULT-выражении 'define_attr'. Если атрибут, чье значение определяется - числовой, то VALUE должно быть строка, содержащая неотрицательное целое число (обычно в этом случае используется 'const_int'). Иначе, оно должно содержать одно из корректных значений атрибута. `(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)' `(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' - строка, означающая выражение на С, которое оставляет глобальной переменной. `(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', то это ссылка на значение переменной транслятора `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') (attr_flag 'backward'))]) Флаги `forward' и `backward' - ложь, если текущий планируемый 'insn' не является условным переходом. Флаги `very_likely' и `likely' истинны, если планируемый 'insn' не является условным переходом. Флаги `very_unlikely' и `unlikely' ложны, если планируемый 'insn' не является условным переходом. и не имеет значения для других проходов транслятора. 5.15.3. Установка значения атрибута для insns. Значение, присвоенное атрибуту insn, предварительно определено тем, какой из образцов согласован с этим insn (или который `define_peephole' сгенерировал его). Каждый `define_insn' и `define_peephole' может иметь необязательный параметр, определяющий значения атрибутов для соответствующих insn. Атрибуту, не определенному ни в каком insn, устанавливается значение использование атрибутов 'по умолчанию', дает возможность определять значения только одного-двух атрибутов большинства insn образцов, что видно в примере из следующего раздела. Последний аргумент `define_insn' и `define_peephole' (его можно не использовать) - это массив выражений, каждое из которых определяет значение единственного атрибута. Наиболее общий способ присвоения значения атрибуту - это использование выражения `set', первый операнд которого - `attr'-выражение, задающее имя устанавливаемого аргумента. Второй операнд `set' - выражение задающее значение атрибута, (см. Выражения). Когда значение атрибута зависит от атрибута `alternative' (то есть, который является соответствующим вариантом в ограничении insn), может использоваться выражение `set_attr_alternative '. Это позволяет специфицировать один вектора атрибутного выражения для всех вариантов. Когда не требуется общность произвольных атрибутных выражений, может использоваться более простое выражение `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') (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', определенного в машинной команды. 5.15.4. Примеры спецификаций атрибута Разумное использование значений по умолчанию важно для эффективного использовании insn атрибутов. Обычно, insns разделены на 'типы' и атрибут, обычно называемый 'type', используемый, для представления это значение. Этот атрибут обычно используется только, чтобы определить значение по умолчанию для других атрибутов. Пример разъяснит это использование. Предположим, что мы имеем RISC машину с кодом условия и в которой в регистрах выполняются только операции с полным словом. Давайте предположим, что мы можем разделить все insns на загружающие, запоминающие, выполняющие (целые) арифметические операции, операции с плавающей точкой и условные. Сейчас мы займемся определением действия 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.15.5. Вычисление Длины Insn На многих машинах предусмотрены многочисленные типы инструкций ветвления, каждый для своей длины условного смещения. В большинстве случаев, ассемблер выберет правильную инструкцию. Однако, когда ассемблер не может этого сделать, 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)' Если определена, то она меняет длину, назначенную для инструкции 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)))]) 5.15.6. Постоянные атрибуты Специальная форма `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.15.7. Планирование слота задержки Механизм insn атрибутов может быть использован для определения требований к слотам задержки, если на целевой машине такие существуют. Инструкция должна требовать слот задержки, если какие-то инструкции физически расположенные за ней, выполняются так, как если бы были перед ней. Классическими примерами являются инструкции перехода и вызова, которые часто выполняют следующую инструкцию до того, как выполнен переход или вызов. На некоторых машинах инструкции условного перехода могут дополнительно не будет выполнена для определенных ветвей прихода. Поддерживаются оба типа инструкций: те которые аннулируют, если ветвь - истина, и тех, которые аннулируют, если ложь. Планирование слота задержки отличается от планирования инструкций тем, что нуждается ли инструкция в слоте задержки или нет, зависит только от типа сгенерированной инструкции и не зависит от данных передаваемых между инструкциями. Смотрите следующий раздел для обсуждения планирования инструкций, зависящего от данных. Требование 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-а, для которого верны хотя бы два из тестов выражений `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.15.8. Определения функциональных модулей На большинстве RISC машин имеются команды, чьи результаты недоступны для определенного числа циклов. В общем случае, команды загружают данные из памяти. На многих машинах случится pipeline stall, если данные запрошены слишком рано после команды загрузки. Кроме того, многие более новые микропроцессоры имеют многофункциональные модули - обычно один для целого числа и один для плавающей точки, и часто, когда требуемый результат еще не готов, будет возникать pipeline stall. Описания в этом разделе позволяют определить, сколько времени должно протечь между выполнением команды и временем, когда используется результат. Они также позволяют определить, когда выполнение команды задержит выполнение подобных команд. Для целей определения в этом разделе, машина разделена на 'функциональные модули', каждый из которых выполняет специфический класс команд в порядке команду за каждый цикл и позволяют результату использоваться в преуспевающей команде (обычно через пересылку) не должны быть определены. Классические 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 может использовать более чем один функциональный модуль, а функциональный модуль может быть определен в более чем одном READY-DELAY - целое число, которое определяет количество циклов, после которого результат команды может использоваться без всякой задержки. ? 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 либо одиночная либо двойная точность, но не обе. Иожет быть определено следующее: (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_func╜ tion_unit'. Недавно мы обратили внимание, что эти определения не могут позволять моделирование некоторых новейших "супер скалярных" процессоров, которые имеют insn, использующие много pipeline модулей. Эти insn могут служить причиной потенциального конфликта для второго модуля, используемого в течение их выполнения, и не существует способа представления этого конфликта. Мы приветствуем любые примеры того, как происходят конфликты функциональных модулей в таких процессорах и предположения по их представлению. 6. Target Description Macros В дополнение к файлу 'MACHINE.md', машинное описание включает файл С-заголовка, которому традиционно дано имя 'MACHINE.h'. Этот файл заголовка определяет многочисленные макрокоманды, которые передают информацию относительно целевой машины, не вписывающеюся в схему файла `.md'. Файл 'tm.h' должен быть связан с 'MACHINE.h'. Файл заголовка 'config.h' включает 'tm.h' и большинство исходных файлов транслятора так же включают 'config.h'. 6.1. Управление Драйвером Трансляции " gcc " Вы можете управлять драйвером трансляции. Выражение C, которое определяет, имеет ли опция " -CHAR " параметры. Значение должно быть числом параметров опции, и нулем, если опций несколько. По умолчанию эта макрокоманда определяется, чтобы правильно обрабатывать стандартные опции. Вы не должны определять ее, если Вы не собираетесь добавлять дополнительные опции, которые имеют параметры. Выражение C, которое определяет, имеет ли опция " -NAME " параметры. Значение должно быть числом параметров опции, и нулем, если опций несколько. Эта макрокоманда скорее, чем " SWITCH_TAKES_ARG ", используется для многосимвольных имен опции. По умолчанию, эта макрокоманда определена как " DEFAULT_WORD_SWITCH_TAKES_ARG ", которая правильно обрабатывает стандартные опции. Вы не должны не определять " WORD_SWITCH_TAKES_ARG ", если Вы не собираетесь добавлять дополнительные опции, которые имеют параметры. Всякое переопределение должно вызывать " DEFAULT_WORD_SWITCH_TAKES_ARG " и затем проверять дополнительные опции. Строковое выражение C, которое является непустым, если компоновщику требуется пробел между опцией " -L " или " -o " и ее параметром. Если эта макрокоманда не определена, значение по умолчанию - 0. Строковая константа C, которая сообщает программе драйвера GNU CC опции для передачи CPP. Она может также указывать, как транслировать опции, которые Вы задали GNU CC, в опции GNU CC для передачи CPP. Не определяйте эту макрокоманду, если не требуется ничего делать. Если эта макрокоманда определена, препроцессор не будет определять встроенную макрокоманду " __SIZE_TYPE__ ". Макрокоманда " __SIZE_TYPE__ " будет определена вместо этого " CPP_SPEC ". Она должна быть определена, если " SIZE_TYPE " зависит от машинно-зависимый флагов, которые не доступны для препроцессора. Иначе, она не должна быть определена. Если эта макрокоманда определена, препроцессор не будет определять встроенную макрокоманду " __PTRDIFF_TYPE__ ". Макрокоманда " __PTRDIFF_TYPE__ " будет определена вместо этого " CPP_SPEC ". Она должна быть определена, если " PTRDIFF_TYPE " зависит от машинно-зависимый флагов, которые не доступны для препроцессора. Иначе, она не должна быть определена. Строковая константа C, которая сообщает программе драйвера GNU CC опции для передачи CPP. По умолчанию эта макрокоманда определена, чтобы передать опцию " -D__CHAR_UNSIGNED__ " CPP, если " char " будет обрабатываться " cc1 " как " unsigned char ". Не определяйте эту макрокоманду, если Вам не нужно перекрывать определение по умолчанию. Строковая константа C, которая сообщает программе драйвера GNU CC опции для передачи " cc1 ". Она может также определять, как транслировать опции, которые Вы задали GNU CC, в опции для GNU CC для передачи " cc1 ". Не определяйте эту макрокоманду, если не требуется ничего делать. Строковая константа C, которая сообщает программе драйвера GNU CC опции для передачи " cc1plus ". Она может также определять, как транслировать опции, которые Вы задали GNU CC, в опции для GNU CC для передачи " cc1plus ". Не определяйте эту макрокоманду, если не требуется ничего делать. Строковая константа C, которая сообщает программе драйвера GNU CC опции для передачи ассемблеру. Она может также определять, как транслировать которые Вы задали GNU CC, в опции для GNU CC для передачи ассемблеру. Для примера см. файл " sun3.h ". Не определяйте эту макрокоманду, если не требуется ничего делать. Строковая константа C, которая сообщает программе драйвера GNU CC, как выполнять программы, которые производят чистку после нормального ассемблера. Обычно это не является необходимым. Для примера см. файл " mips.h ". Не определяйте эту макрокоманду, если не требуется ничего делать. Строковая константа C, которая сообщает программе драйвера GNU CC опции для передачи компоновщику. Она может также определять, как транслировать опции, которые Вы задали GNU CC, в опции для GNU CC для передачи компоновщику. Не определяйте эту макрокоманду, если не требуется ничего делать. Другая строковая константа C, используемая подобно " LINK_SPEC ". Различие заключается в том, что " LIB_SPEC " используется в конце команды, задаваемой компоновщику. Если эта макрокоманда не определена, значение по умолчанию - то, при условии, что загрузки Стандарт C библиотека из обычного места. См. " gcc.c ". Другая строковая константа C, которая сообщает программе драйвера GNU CC, как и когда помещать ссылку на " libgcc.a " в командную строку компоновщика. Эта константа помещается и до, и после значения " LIB_SPEC ". Если эта макрокоманда не определена, драйвер GNU CC по умолчанию передает строку " -lgcc " компоновщику, если опция " -shared " не определена. Другая строковая константа C, используемая подобно " LINK_SPEC ". Различие заключается в том, что " STARTFILE_SPEC " используется в самом начале команды, задаваемой компоновщику. Если эта макрокоманда не определена, по умолчанию стандартный файл запуска C загружается из обычного места. См. " gcc.c ". Другая строковая константа C, используемая подобно " LINK_SPEC ". Различие заключается в том, что " ENDFILE_SPEC " используется в самом конце команды, задаваемой компоновщику. Не определяйте эту макрокоманду, если не требуется ничего делать. Определите эту макрокоманду, если программа драйвера сама должна искать библиотеку " libgcc.a " и не должна передавать опции " -L " компоновщику. Если Вы не определяете эту макрокоманду, программа драйвера передаст параметр " -lgcc ", чтобы компоновщик произвел поиск и передаст ему опции " -L ". Определите эту макрокоманду, если программа драйвера сама должна искать библиотеку " libgcc.a ". Если Вы не определяете эту макрокоманду, программа драйвера передаст параметр " -lgcc ", чтобы компоновщик произвел поиск. Эта макрокоманда подобна " LINK_LIBGCC_SPECIAL ", за исключением того, что она не воздействуют на " -L " опции. Определите эту макрокоманду как выражение C для инициализатора массива строк, чтобы сообщить программе драйвера, какие опции являются значениями по умолчанию в данной ситуации и, таким образом, не нуждаются в специальной обработке при использовании " MULTILIB_OPTIONS ". Не определяйте эту макрокоманду, если " MULTILIB_OPTIONS " не определен во фрагменте makefile адресата или если ни одна из опций, перечисленных в " MULTILIB_OPTIONS ", не имеет значения по умолчанию. * См.: Фрагмент адресата::. Определите эту макрокоманду, чтобы сообщить " gcc " что он должен транслировать префикс " -B " в опцию " -L " компоновщика, если префикс указывает абсолютное имя файла. Определите эту макрокоманду как строковую константу C, если Вы хотите изменить стандартный выбор " /usr/local/lib/gcc-lib/ " как префикс по умолчанию при поиске исполняемых файлов транслятора. Если определено, эта макрокоманда - дополнительный префикс после " STANDARD_EXEC_PREFIX ". " MD_EXEC_PREFIX " не ищется, если используется опция " -b " или транслятор построен как перекрестный. Определите эту макрокоманду как строковую константу C, если Вы хотите изменить стандартный выбор " /usr/local/lib/ " как префикс по умолчанию, чтобы при поиске файлов запуска типа " crt0.o ". Если определена, эта макрокоманда обеспечивает дополнительный префикс после стандартных префиксов. " MD_EXEC_PREFIX " не ищется, если используется опция " -b " или транслятор построен как перекрестный. Если определена, эта макрокоманда обеспечивает еще один префикс после стандартных префиксов. Она не ищется, если используется опция " -b " или транслятор построен как перекрестный (кросс-транслятор). Определите эту макрокоманду как строковую константу C, если Вы хотите установить переменные среды для программ, вызываемых драйвером, таких, как ассемблер и загрузчик. Драйвер передает значение этой макрокоманды " putenv ", чтобы инициализировать необходимые переменные среды. Определите эту макрокоманду как строковую константу C, если Вы хотите изменить стандартный выбор " /usr/local/include " как префикс по умолчанию при поиске локальных файлов заголовка. " LOCAL_INCLUDE_DIR " идет перед " SYSTEM_INCLUDE_DIR " в порядке поиска. Перекрестные трансляторы не используют эту макрокоманду и не ищут " /usr/local/include " или его замену. Определите эту макрокоманду как строковую константу C, если Вы хотите указать свой каталог для поиска файлов заголовка перед стандартным каталогом. " SYSTEM_INCLUDE_DIR " идет перед " STANDARD_INCLUDE_DIR " в порядке поиска. Перекрестные трансляторы не используют эту макрокоманду и не ищут указанный каталог. Определите эту макрокоманду как строковую константу C, если Вы хотите изменить стандартный выбор " /usr/include " как префикс по умолчанию при поиске файлов заголовка. Перекрестные трансляторы не используют эту макрокоманду и не ищут " /usr/include " или его замену. Определите эту макрокоманду, если Вы хотите полностью изменить значение по умолчанию пути поиска включаемых файлов. Заданный по умолчанию путь поиска включает " 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 ". 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. Целевая Спецификация времени выполнения Вот целевые спецификации времени выполнения. Определите ее строковой константой, содержащей " -D " опции для определения предопределенных макрокоманд, которые идентифицируют эту машину и эту систему. Эти макрокоманды будут предопределенными, если не указана опция " -ansi ". Кроме того, предопределен параллельный набор макрокоманд, чьи имена получаются добавлением " __ " в начале и в конце. Эти " __ " макрокоманды разрешаются ANSI стандартом, так что они предопределены независимо от того, указано ли " -ansi ". Например, на Sun можно использовать следующее значение: " -Dmc68000 -Dsun -Dunix " Результат должен определять макрокоманды " __ mc68000 __ ", " __ sun __ " и " __ unix __ " в любом случае, и макрокоманды " mc68000 ", " sun " и " unix ", если не указано " -ansi ". Это объявление должно присутствовать. Этот набор макрокоманд предназначен для того, чтобы позволить параметрам команд транслятора включать или отключать использование опциональных особенностей целевой машины. Например, одно машинное описание служит и для 68000, и для 68020; параметр команды сообщает транслятору, использовать ли ему команды, имеющиеся только на 68020 или нет. Этот параметр команды работает посредством макрокоманды " TARGET_68020 " которая проверяет бит в " target_flags ". Определите макрокоманду " TARGET_FEATURENAME " для каждой такой опции. Ее определение должно проверять бит в " target_flags "; например: *define TARGET_68020 (target_flags & 1) Одним из мест, где используются эти макрокоманды, являются условные выражения образцов команд. Заметьте, как часто " TARGET_68020 " появляется в файле машинного описания 68000, " m68k.md ". Другое место их использования - это определение других макрокоманд в файле" MACHINE.h ". Эта макрокоманда определяет имена опций команды, чтобы устанавливать и очищать биты в " target_flags ". Определение - инициализатор с подгруппировкой для каждой опции команды. Каждая подгруппировка содержит строковую константу, которая определяет имя опции, и число, которое содержит биты для установки в " target_flags ". Отрицательное число указывает на очистку битов; противоположное ему число указывает, какие биты очищать. Фактическое имя опции получается добавлением " -m " к указанному имени. Одна из подгруппировок должна иметь пустую строку. Число в этой группировке - значение по умолчанию для " target_flags ". Любое действие опций начинается с этим значением. Вот пример, который определяет " -m68000 " и " -m68020 " с противоположными значениями и выбирает последний как значение по умолчанию: #define TARGET_SWITCHES \ { { "68020", 1}, \ { "68000", -1}, \ { "", 1}} Эта макрокоманда аналогична " TARGET_SWITCHES ", но определяет имена опций команды, которые имеют значения. Ее определение - инициализатор с подгруппировкой для каждой опции команды. Каждая подгруппировка содержит строковую константу, которая определяет фиксированную часть имени опции и адрес переменной. Переменная типа " char * " устанавливается на переменную часть данной опции, если фиксированная часть соответствует. Фактическое имя опции получается добавлением " -m " к указанному имени. Вот пример, который определяет " -mshort-data-NUMBER ". Если данная опция - " -mshort-data-512 ", переменная " m88k_short_data " будет установлена на строку " "512" ". extern char *m88k_short_data; #define TARGET_OPTIONS \ { { "short-data-", &m88k_short_data } } Эта макрокоманда - оператор 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 ", чтобы принять это во внимание. Эта макрокоманда, если она определена, выполняется только однажды - сразу после того, как все опции команды будут проанализированы. Не используйте эту макрокоманду, чтобы включить различные дополнительные оптимизации для " -O ". Для этого есть " OPTIMIZATION_OPTIONS ". Некоторые машины могут хотеть изменить то, какие оптимизации выполняются для различных уровней оптимизации. Эта макрокоманда, если она определена, выполняется однажды - сразу после того, как уровень оптимизации определен и перед тем, как остаток опций команды проанализируется. Набор значений в этой макрокоманде используется по умолчанию для других опций командной строки. LEVEL - заданный уровень оптимизации; 2, если указано " -O2 ", 1, если указано " -O " , и 0, если ничего не указано. Вы не должны использовать эту макрокоманду, для изменения опций, которые не являются машинно-специфическими. Они должны одинаково зависеть от уровня оптимизации на всех поддериваемых машинах. Используйте эту макрокоманду для разрешения машинно-специфических оптимизаций. * Не используйте " write_symbols " в этой макрокоманде! * Предполагается, что отладочные опции не изменяют сгенерированный код. Определите эту макрокоманду, если отладка может выполняться даже без указателя кадра. Если эта макрокоманда определена, GNU CC будет включать опцию " -fomit-frame-pointer " всякий раз, когда указано " -O ". 6.3. Распределение Памяти Обратите внимание, что определения макрокоманд в этой таблице, которые являются размерами или выравниваниями, измеряемыми в битах, не обязаны быть постоянными. Они Это могут быть выражения C, который ссылаются на статические переменные, такие как "target_flags ". См.: Адресат времени выполнения::. Задайте этому макросу значение 1, если старший бит в байте имеет наименьший номер; иначе задайте ему значение 0. Это означает, что побитовые команды отсчитывают биты от старшего. Если машина не имеет побитовых команды, то значение этого макроса не играет роли. Этот макрос не обязан быть константой. Этот макрос не воздействует на способ упаковки полей структур, байтами или словами; этим управляет " BYTES_BIG_ENDIAN ". Задайте этому макросу значение 1, если старший байт в слове имеет меньший номер. Этот макрос не обязан быть константой. Задайте этому макросу значение 1 если в объекте из нескольких слов старшее слово имеет наименьший номер. Это применяется и к ячейкам памяти, и к регистрам; GNU CC всегда считает,что порядок слов в памяти такой же, как и в регистрах. Этот макрос не обязан быть константой. Определите эту макрокоманду, если WORDS_BIG_ENDIAN - не константа. Это должно быть постоянное значение с тем же самым значением, что и WORDS_BIG_ENDIAN, которое будет использоваться только при компиляции libgcc2.c. Обычно значение устанавливается, основываясь на определениях препроцессора. Задайте этому макросу значение 1, если числа с плавающей точкой " DFmode ", " XFmode " или " TFmode " сохраняются в памяти со словом, содержащим знаковый разряд по наименьшему адресу; иначе задайте ему значение 0. Этот макрос не обязан быть константой. Вы не должны определять этот макрос, если упорядочение такое же, как для целых чисел из нескольких слов. Задайте этому макросу значение, равное числу битов в минимальной адресуемой ячейке памяти (байте); обычно 8. Число битов в слове; обычно 32. Максимальное число битов в слове. Если это неопределено, значение по умолчанию - " BITS_PER_WORD ". Иначе, это - константа, равная наибольшему значению, которое может иметь " BITS_PER_WORD " во время выполнения. Число байтов в слове; обычно 4. Минимальное число байтов в слове. Если это неопределено, значение по умолчанию - " UNITS_PER_WORD ". Иначе, это - константа, равная наименьшему значению, которое может иметь " UNITS_PER_WORD " во время выполнения. Длина указателя в битах. Вы не должны указывать значение, большее, чем длина " Pmode ". Если оно не равно ширине " Pmode ", Вы должны определить " POINTERS_EXTEND_UNSIGNED ". Выражение C, чье значение отлично от нуля, если указатели, которые должны быть расширены от длины " POINTER_SIZE " до длины " Pmode ", следует расширять знаком, и ноль, если их следует расширять нулем. Вы не должны определять эту макрокоманду, если " POINTER_SIZE " равно длине " Pmode ". Макрокоманда для модификации M и UNSIGNEDP, когда объект, чей тип - TYPE и который имеет указанный тип и знаковость, должен быть сохранен в регистре. Эта макрокоманда вызывается только тогда, когда TYPE - скалярный тип. На большинстве RISC машин, которые имеют операции для манипуляции только полными регистрами, определите эту макрокоманду, чтобы установить M в " word_mode ", если M - целочисленный тип, более узкий, чем " BITS_PER_WORD ". В большинстве случаев расширяться должны только целочисленные типы, потому что большая точность широких операций с плавающей точкой обычно дороже, чем для их более узких аналогов. Для большинства машин макроопределение не изменяет UNSIGNEDP. Однако некоторые машины имеют команды, которые предпочтительно обрабатывают некоторые как знаковые, так и беззнаковые типы. Например, на DEC Alpha 32-битные команды загрузки из памяти и 32-битные команды сложения расширяются знаком до 64 бит. На таких машинах устанавливайте UNSIGNEDP в зависимости от того, какой вид расширения более эффективен. Не определяйте эту макрокоманду, если она не должна изменять M. Определите эту макрокоманду, если продвижение, описанное " PROMOTE_MODE ", должно быть также сделано для исходящих параметров функции. Определите эту макрокоманду, если продвижение, описанное " PROMOTE_MODE ", должно быть также сделано для возвращаемых значений функции. Если эта макрокоманда определена, " FUNCTION_VALUE " должна выполнить те же самые продвижения, что и сделанные " PROMOTE_MODE ". Определите эту макрокоманду, если продвижение, описанное " PROMOTE_MODE " должно быть выполнено *только* для исходящих параметров функции или возвращаемых функцией значений, как указано " PROMOTE_FUNCTION_ARGS " и " PROMOTE_FUNCTION_RETURN ", соответственно. Нормальное выравнивание, требуемое для параметров функции в стеке, в битах. Все параметры стека получают по крайней мере это выравнивание независимо от типа данных. На большинстве машин оно совпадает с размером целого числа. Определите эту макрокоманду, если Вы хотите сохранить некоторое выравнивание для указателя стека. Определение - выражение C для желаемого выравнивания (измеряемое в битах). Если " PUSH_ROUNDING " не определено, стек будет всегда выравниваться на указанную границу. Если " PUSH_ROUNDING " определено и определяет менее строгое выравнивание, чем " STACK_BOUNDARY ", выравнивание может быть потеряно на короткое время при записи параметров в стек. Выравнивание, требуемое для точки входа в функцию, в битах. Самое большое выравнивание, которое может потребоваться какому-либо типу данных на этой машине, в битах. Самое большое выравнивание, которое может потребоваться какому-либо полю структуры на этой машине, в битах. Если определено, оно отменяет " BIGGEST_ALIGNMENT " для полей структур. Самое большое выравнивание, поддерживаемое форматом объектного файла на этой машине. Используйте эту макрокоманду, чтобы ограничить выравнивание, которое может быть определено с использованием конструкции " __ attribute __ ((aligned (N))) ". Если не определено, значением по умолчанию является " BIGGEST_ALIGNMENT ". Если определено, выражение C, чтобы вычислить выравнивание для статической переменной. TYPE - тип данных, а BASIC-ALIGN - выравнивание, которое объект имел бы без этой макрокоманды. Значение этой макрокоманды используется вместо этого выравнивания для выравнивания объекта. Если эта макрокоманда не определена, то используется BASIC- ALIGN. Одно из применений этой макрокоманды заключается в увеличении выравнивания данных среднего размера, чтобы все они поместились на меньшем количестве строк кэша. Другое - выравнивать символьные массивы на границу слова, чтобы вызовы " strcpy ", копирующие константы в символьные массивы, можно было сделать встроенными. Если определено, выражение C для вычисления выравнивания для константы, которая помещается в память. CONSTANT - константа, а BASIC-ALIGN - выравнивание, которое объект имел бы без этой макрокоманды. Значение этой макрокоманды используется вместо этого выравнивания для выравнивания объекта. Если эта макрокоманда не определена, то используется BASIC- ALIGN. Типичным использованием этой макрокоманды является увеличение выравнивание для строковой константы до выравнивания на границу слова, так, чтобы вызовы " strcpy ", копирующие константы в символьные массивы, можно было сделать встроенными. Выравнивание в битах, задаваемое битовому полю структуры, которое следует за пустым полем, типа " int: 0; ". Обратите внимание, что " PCC_BITFIELD_TYPE_MATTERS " также воздействует на выравнивание, которое получается после пустого поля. Число битов, которым должен быть кратным размер любой структуры или объединения. Размер каждой структуры и объединения округляется вверх до его кратного. Если Вы не определяете эту макрокоманду, значение по умолчанию совпадает с " BITS_PER_UNIT ". Задайте этому макросу значение 1, если команды не будут работать, в случае если заданные данные не будут иметь номинального выравнивания. Если команды в этом случае просто будут работать медленнее, задайте этому макросу значение 0. Определите это, если Вы хотите имитировать способ многих других трансляторов C обработки выравнивания битовых полей и структур, которые содержат их. Поведение состоит в том, что тип, заданный для битового поля (" int ", " short " или другой целочисленный тип) налагает выравнивание для всей структуры, как будто структура действительно содержит обычное поле этого типа. Кроме того, битовое поле помещается внутри структуры так, чтобы это поместилось бы внутри такого поля, не пересекая его границы. Таким образом, на большинстве машин битовое поле, чей тип описан как " 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; }; 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 ". Подобно PCC_BITFIELD_TYPE_MATTERS, за исключением того, что эффект ограничивается выравниванием битовых полей внутри структуры. Определите эту макрокоманду как выражение для полного размера структуры (задаваемой STRUCT как узел дерева), если вычисленный размер полей - SIZE, а выравнивание - ALIGN. Значение по умолчанию - округление SIZE вверх до числа, кратного ALIGN. Определите эту макрокоманду как выражение для выравнивания структуры (задаваемой STRUCT как узел дерева), если выравнивание, вычисленное обычным путем - COMPUTED, а выравнивание, явно указанное - SPECIFIED. Значением по умолчанию является SPECIFIED, если оно больше; иначе используется меньшее из COMPUTED и " BIGGEST_ALIGNMENT " Целочисленное выражение для размера в битах самого большого целого числа машинного типа, который должен фактически использоваться. Все целочисленные машинные типы этого размера или меньше могут использоваться для структур и объединений с соответствующими размерами. Если эта макрокоманда не определена, предполагается " GET_MODE_BITSIZE (DImode) ". Оператор C для проверки допустимости значения VALUE (типа " double ") для типа MODE. Это означает, что Вы проверяете, входит ли VALUE в диапазон возможных значений для типа MODE на этой целевой машине. Режим MODE - всегда тип класса " MODE_FLOAT ". OVERFLOW отлично от нуля, если значение оказывается вне диапазона. Если VALUE недопустимо или если OVERFLOW отлично от нуля, Вы должны установить OVERFLOW в 1 и затем присвоить какое-нибудь допустимое значение VALUE. Прохождение недопустимого значения через транслятор может произвести неправильный код ассемблера, который может даже повлечь аварию ассемблеров Unix. Эта макрокоманда не обязана определяться, если в ней нет потребности. Код, отличающий формат с плавающей точкой на целевой машине. Имеется три определенных значения: Этот код указывает формат с плавающей точкой IEEE. Это - значение по умолчанию; нет необходимости определять эту макрокоманду, если формат - IEEE. Этот код указывает специфический формат, используемый на Vax. Этот код указывает любой другой формат. Значение этой макрокоманды сравнивается с " HOST_FLOAT_FORMAT " (* См.: Конфигурация::.), чтобы определить, имеет ли целевая машина тот же самый формат, что и главная машина. Если на поддерживаемых машинах фактически используются какие-то другие форматы, для них должны быть определены новые коды. Порядок слов, составляющих значение с плавающей запятой, записанное в памяти, управляется " FLOAT_WORDS_BIG_ENDIAN " для целевой машина и " HOST_FLOAT_WORDS_BIG_ENDIAN " для главной ЭВМ. 6.4. Формат Исходных Типов Данных Языка Эти макрокоманды определяют размеры и другие характеристики стандартных базисных типов данных, используемых в компилируемых программах. В отличие от макрокоманд предыдущего раздела, они обращаются скорее к специфическим особенностям C и родственных языков, чем к фундаментальным аспектам формата хранения данных. Выражение C для размера в битах типа " int " на целевой машине. Если Вы не определяете его, значение по умолчанию - одно слово. Максимальное число для размера в битах типа " int " на целевой машине. Если оно не определено, значение по умолчанию - " INT_TYPE_SIZE ". Иначе, это - постоянное значение, которое является самым большим значением, какое " INT_TYPE_SIZE " может иметь во время выполнения программы. Оно используется в " cpp ". Выражение C для размера в битах типа " short " на целевой машине. Если Вы не определяете его, значение по умолчанию - половина слова. (Если оно меньше, чем одна адресуемая единица памяти, то оно округляется вверх до ее размера.) Выражение C для размера в битах типа " long " на целевой машине. Если Вы не определяете его, значение по умолчанию - одно слово. Максимальное число для размера в битах типа " long " на целевой машине. Если оно не определено, значение по умолчанию - " LONG_TYPE_SIZE ". Иначе, это - постоянное значение, которое является самым большим значением, какое " LONG_TYPE_SIZE " может иметь во время выполнения программы. Оно используется в " cpp ". Выражение C для размера в битах типа " long long " на целевой машине. Если Вы не определяете его, значение по умолчанию - два слова. Если Вы хотите поддерживать GNU Ada на вашей машине, это значение должно быть по крайней мере 64. Выражение C для размера в битах типа " char " на целевой машине. Если Вы не определяете его, значение по умолчанию - четверть слова. (Если оно меньше, чем одна адресуемая единица памяти, то оно округляется вверх до ее размера.) Максимальное число для размера в битах типа " char " на целевой машине. Если оно не определено, значение по умолчанию - " CHAR_TYPE_SIZE ". Иначе, это - постоянное значение, которое является самым большим значением, какое " CHAR_TYPE_SIZE " может иметь во время выполнения программы. Оно используется в " cpp ". Выражение C для размера в битах типа " float " на целевой машине. Если Вы не определяете его, значение по умолчанию - одно слово. Выражение C для размера в битах типа " double " на целевой машине. Если Вы не определяете его, значение по умолчанию - два слова. Выражение C для размера в битах типа " long double " на целевой машине. Если Вы не определяете его, значение по умолчанию - два слова. Выражение, равное 1 или 0, в зависимости от того, должен тип " char " быть знаковым или беззнаковым по умолчанию. Пользователь всегда может отменить это значение по умолчанию с опциями " -fsigned-char " и " -funsigned-char ". Выражение C, определяющее, давать ли типу " enum " только столько байтов, сколько требуется, чтобы представить диапазон возможных значений этого типа. Значение, отличное от нуля, означает делать это; значение ноля означает, что все " enum " типы должны распределяться подобно " int ". Если Вы не определяете макрокоманду, значение по умолчанию - 0. Выражение C для строки, описывающей имя типа данных, используемого для значений размера. Тип " size_t " определяется с использованием содержимого строки. Строка может содержать больше одного ключевого слова. Если это так, отделяйте их пробелами, и пишите сначала ключевое слово, указывающее длину, затем " unsigned ", если нужно, и в заключение " int ". Строка должна точно соответствовать одному из имен типа данных, определенных в функции " init_decl_processing " в файле " c-decl.c ". Вы не можете опускать " int " или изменять порядок - это приведет к аварии при запуске транслятора. Если Вы не определяете эту макрокоманду, значение по умолчанию - " "long unsigned int " ". Выражение C для строки, описывающей имя типа данных, используемого для значений разности двух указателей. Тип " ptrdiff_t " определяется с использованием содержимого строки. См. " SIZE_TYPE " выше для более подробной информации. Если Вы не определяете эту макрокоманду, значение по умолчанию - " " long int " ". Выражение C для строки, описывающей имя типа данных, используемого для длинных символов. Тип " wchar_t " определяется с использованием содержимого строки. См. " SIZE_TYPE " выше для более подробной информации. Если Вы не определяете эту макрокоманду, значение по умолчанию - " "int" ". Выражение C для размера в битах типа данных для длинных символов. Это используется в " cpp ", который не может использовать " WCHAR_TYPE ". Максимальное число для размера в битах типа данных для длинных символов. Если оно не определено, значение по умолчанию - " WCHAR_TYPE_SIZE ". Иными словами, это - константа, которое является самым большим значением, которое " WCHAR_TYPE_SIZE " может иметь во время выполнения программы. Это используется в " cpp ". Определите эту макрокоманду, если тип селекторов Objective C должен быть " int ". Если эта макрокоманда не определена, то селекторы должны иметь тип " struct objc_selector * ". Определите эту макрокоманду, если транслятор может группировать все селекторы вместе в вектор и использовать только одну метку в начале вектора. Иными словами, транслятор должен дать каждому селектору собственную ассемблерную метку. На некоторых машинах важно иметь отдельную метку для каждого селектора, потому что это дает возможность компоновщику устранить двойные селекторы. Константное выражение C для целого значения управляющей последовательности " \a ". Константное выражение C для целых значений управляющих последовательностей " \b ", " \t " и " \n ". Константное выражение C для целых значений управляющих последовательностей " \v ", " \f " и " \r ". 6.5. Использование Регистров В этом разделе объясняется, как описывать, какие регистры имеет целевая машина и как (вообще) они могут использоваться. Описание того, какие регистры могут использовать конкретные команды, сделано с классами регистров; см. *Классы Регистров::. По поводу информации об использовании регистров для обращения к кадру стека, см. *Регистры Кадра::. О передаче значений в регистрах см. *Параметры Регистров::. О возврате значений в регистрах, см. *Возврат скаляров::. 6.5.1. Базовые Характеристики Регистров Регистры имеют различные характеристики. Число аппаратных регистров, известных транслятору. Они получают номера от 0 до " FIRST_PSEUDO_REGISTER-1 "; таким образом, первому псевдорегистру действительно присваивается номер " FIRST_PSEUDO_REGISTER ". Инициализатор, который говорит, какие регистры используются для фиксированных целей на протяжении всего оттранслированного кода и, следовательно, недоступны для общего распределения. Они могут включать указатель стека, указатель кадра (за исключением машинах, где он может использоваться как общий регистр, если указатель кадра не нужен), программный счетчик на машинах, где он рассматривается как адресуемый регистр, и любой другой пронумерованный регистр со стандартным использованием. Эта информация выражается как последовательность чисел, разделяемых запятыми и заключенная в фигурные скобки. N-е число равно 1, если регистр N фиксирован, и 0 в противном случае. Таблица, инициализированная этой макрокомандой, и таблица, инициализированной следующей, могут быть изменены во время выполнения программы как автоматически, под действием макрокоманды " CONDITIONAL_REGISTER_USAGE ", так и пользователем, при помощи опций команды " -ffixed-REG ", " -fcall-used-REG " и " -fcall-saved-REG ". Подобно " FIXED_REGISTERS ", но имеет 1 для каждого регистра, который затирается (вообще говоря) обращениями к функции так же, как для фиксированных регистров. Эта макрокоманда, следовательно, идентифицирует регистры, которые не доступны для общего распределения значений, которые должны жить во время обращений к функции. Если регистр имеет 0 в " CALL_USED_REGISTERS ", транслятор автоматически сохраняет его на входе в функцию и восстанавливает его на выходе из функции, если регистр используется внутри функции. Ноль или более операторов C, которые могут условно изменять две переменных " fixed_regs " и " call_used_regs " (обе - типа " char[] ") после того, как они были инициализированы двумя предыдущими макрокомандами. Это необходимо в случае, если фиксированные или затиаремые при вызове регистры зависят от целевых флажков. Не определяйте эту макрокоманду, если не требуется ничего делать. Если использование всего класса регистров зависит от целевых флагов, Вы можете сообщить об этом GCC, используя эту макрокоманду для установки " fixed_regs " и " call_used_regs " в 1 для каждого из регистров в классах, которые не должны использоваться GCC. Также определите макрокоманду " REG_CLASS_FROM_LETTER " так, чтобы она возвращала " NO_REGS ", если она вызывается с символом для класса, который не должен использоваться. (Однако, если этот класс не включен в " GENERAL_REGS " и все образцы insn, чей ограничения разрешают этот класс, управляются целевыми переключателями, то GCC будет автоматически избегать использования этих регистров, когда целевые переключатели запрещают это.) Если эта макрокоманда определена и имеет значение, отличное от нуля, это означает, что " setjmp " и связанные с ней функции не могут сохранить регистров, или что " longjmp " не может восстановить их. В качестве компенсации транслятор избегает помещать переменные в регистры в функциях, использующих " setjmp ". Определите эту макрокоманду, если целевая машина имеет окна регистров. Это выражение C возвращает номер регистра, который видит вызываемая функция, в зависимости от номера регистра OUT, который видит вызывающая функция. Возвратите OUT, если номер регистра OUT не выходящий (outbound) регистр. Определите эту макрокоманду, если целевая машина имеет окна регистров. Это выражение C возвращает номер регистра, который видит вызывающая функция, в зависимости от номера регистра IN, который видит вызываемая функция. Возвратите IN, если номер регистра IN не входящий (inbound) регистр. 6.5.2. Порядок Распределения Регистров Регистры распределяются по порядку. Если определено, инициализатор для вектора целых чисел, содержащих номера аппаратных регистров в порядке, в котором GNU CC предпочтительно должен использовать их (от наиболее прдпочтительных к наименее). Если эта макрокоманда не определена, самыми первыми используются регистры с самыми маленькими номерами ( все, кроме эквивалентных). Одно из применений этой макрокоманды - на машинах, где регистры с самыми большими номерами всегда должны сохраняться и команды сохранения нескольких регистров поддерживают только последовательности последовательных регистров. На таких машинах определите " REG_ALLOC_ORDER " как инициализатор, заносящий в список первыми годные для распределения регистры с самыми большими номерами. Оператор C (без точки с запятой) для выбора порядка распределения аппаратных регистров для псевдорегистров, локальных для базового блока. Сохраните желательный порядок регистров в массиве " reg_alloc_order ". Элемент 0 должен быть регистр, распределяемым первым; элемент 1 - следующим, и так далее. Тело макрокоманды не должно делать никаких предположений относительно содержимого " reg_alloc_order " перед выполнением макрокоманды. На большинстве машин нет необходимости определять эту макрокоманду. 6.5.3. Как Располагать Значения В Регистрах В этом разделе обсуждаются макрокоманды, которые описывают, какие виды значений (конкретно, какие машинные типы) каждый регистр может содержать, и сколько последовательных регистров необходимо для данного типа. Выражение C для числа последовательных аппаратных регистров, начиная с регистра с номером REGNO, требуемого для хранения значения типа MODE. Например, на машине, где все регистры - ровно одно слово, эту макрокоманду следует определить так: #define HARD_REGNO_NREGS(REGNO, MODE) \ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \ / UNITS_PER_WORD)) Выражение C, которое отлично от нуля, если допустимо сохранение значения типа MODE в аппаратном регистре с номером REGNO (или в нескольких регистрах, начиная с него). Для машины, где все регистры одинаковы, определения должно выглядеть так: #define HARD_REGNO_MODE_OK (REGNO, MODE) 1 Для этой макрокоманды не является необходимой проверка номеров фиксированных регистров, потому что механизм распределения рассматривает их как всегда занятые. На некоторых машинах, значения двойной точности должны храниться в четных/нечетных парах регистров. Способ сделать это заключается в том, чтобы запретить в этой макрокоманде нечетные номера регистров для таких типов. Минимальное требование для типа, чтобы он подходил для регистра, заключается в том, что образец команды " movMODE " поддерживает перемещение между регистром и любым другим аппаратным регистром, для которого этот тип подходит; и это перемещение значения в регистр и обратно не изменяет его. Так как та же самая команда, используемая для передвижения " SImode ", будет работать для всех более узких целочисленных типов, для " HARD_REGNO_MODE_OK " не обязательно на любой машине различать эти типы, если Вы определяете образцы " movhi ", и т.д., чтобы воспользоваться преимуществом этого. Это полезно из-за взаимодействия между " HARD_REGNO_MODE_OK " и " MODES_TIEABLE_P "; очень желательно, чтобы все целочисленные типы были связываемы таким образом. Многие машины имеют специальные регистры для арифметики с плавающей точкой. Часто люди считают, что машинные типы с плавающей точкой позволяются только в регистрах с плавающей точкой. Это не так. Любые регистры, которые могут хранить целые числа, могут безопасно *сохранять* машинный тип с плавающей точкой, независимо от того, может или нет выполняться в этих регистрах плавающая арифметика. Целочисленные команды перемещения могут использоваться и для перемещения этих значений. На некоторых машинах, однако, верно обратное: машинные типы с фиксированной точкой не могут находиться в плавающих регистрах. Это так, если плавающие регистры нормализуют любое значение, сохраненное в них, потому что сохранение не-плавающего значения там исказило бы его. В этом случае " HARD_REGNO_MODE_OK " должен запретить машинные типы с фиксированной точкой в плавающих регистрах. Но если плавающие регистры не нормализуются автоматически, если Вы можете сохранять любой набор битов в регистр и восстанавливать его без изменений, то любой машинный тип может храниться в плавающем регистре, и Вы можете определять с учетом этого эту макрокоманду. Основное значение специальных плавающих регистров - это то, что к ним применимы команды арифметики с плавающей точкой. Однако это не имеет никакого отношения к " HARD_REGNO_MODE_OK ". Вы обрабатываете это, записывая соответствующие ограничения для этих команд. На некоторых машинах доступ к плавающим регистрам особенно медленный, так что лучше хранить значение во кадре стека, чем в таком регистре, если с ним не выполняется команд плавающей арифметики. Поскольку плавающие регистры не содержатся в классе " GENERAL_REGS ", они не будут использоваться, если этого не потребует какое-нибудь ограничение образца. Выражение C, которое отлично от нуля, если желательно выбирать распределение регистров так, чтобы избегать команд перемещения между a значениями типа MODE1 и значениями типа MODE2. Если " HARD_REGNO_MODE_OK (R, MODE1) " и " HARD_REGNO_MODE_OK (R, MODE2) " являются различным для любого R, то " MODES_TIEABLE_P (MODE1, MODE2) " должно быть нулем. 6.5.3.1. Обработка leaf-функций На некоторых машинах, leaf-функция (то есть та, которая не производит никаких вызовов) может выполняться более эффективно, если не делает собственного окна регистров. Часто это означает, что она требует, чтобы параметры были в регистрах, в которые их передает вызывающая функция, вместо регистров, через которые они обычно поступают. Специальная обработка для leaf-функций применяется только когда другие условия выполнены; например, часто они могут использовать только эти же регистры для собственных переменных и временных данных. Мы используем термин "leaf-функция", чтобы обозначить функцию, которая подходит для этой специальной обработки, так что функции без обращений не обязательно - "leaf-функции". GNU CC назначает номера регистрам прежде чем узнает, является ли функция подходящая для обработки как leaf-функция. Так что ему требуется перенумеровать регистры, чтобы получить leaf-функцию. Следующие макрокоманды выполняют это. "LEAF_REGISTERS' ("leaf-регистры") Инициализатор C для вектора, индексированного номерами аппаратных регистров, который содержит 1 для регистра, который является кандидатом на обработку для leaf-функции. Если обработка leaf-функции включает перенумеровку регистров, то регистры, отмеченные выше, должны содержать 1 перед перенумерацией, как GNU CC обычно и распределяет. Регистры, которые будут фактически использоваться в ассемблерном коде, после изменения нумерации, не должны быть отмечены 1 в этом векторе. Определите эту макрокоманду только если целевая пытается оптимизировать обработку leaf-функций. "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.5.4. Регистры, образующие стек На компьютерах, где некоторые из регистров образуют стек (как в 80387 сопроцессоре для 80386), имеются некоторые особенности. Регистры стека обычно записываются, проталкиванием в стек, и пронумерованы относительно вершины стека. В настоящее время, GNU CC может обрабатывать только одну группу стекоподобных регистров, а они должны быть последовательно пронумерованы. Определите ее, если машина имеет стекоподобные регистры. Номер первого стекоподобного регистра. Это - вершина стека. Номер последнего стекоподобного регистра. Этот - дно стека. 6.5.5. Устаревшие Макрокоманды для Управления Использованием Регистров Эти возможности работают не очень хорошо. Они существуют, потому что требуются для генерации правильного кода для 80387 сопроцессора 80386. Они больше не используются в соответствии c машинным описанием и могут быть удалены в более поздней версии транслятора. Не используйте их! Если определена, то это - выражение C, значение которого отлично от нуля, если аппаратный номер регистра REGNO - перекрывающийся регистр. Это означает аппаратный регистр, который перекрывается c аппаратным регистром с другим номером. (Такое перекрытие нежелательно, но иногда позволяет поддерживаться машине, которая иначе не поддерживалась бы.) Эта макрокоманда должна возвратить значение, отличное от нуля, для *всех* регистров, которые накладываются на друг друга. GNU CC может использовать накладывающийся регистр только в некоторых ограниченных случаях. Они могут использоваться для распределения внутри базисного блока и могут быть spilled для перезагрузки; вот и все. Если эта макрокоманда не определена, это означает, что ни один из аппаратных регистров не накладывается друг на друга. Это - обычная ситуация. Если определена, то это - выражение C, значение которого должно быть отлично от нуля, если insn INSN имеет эффект таинственного затирания (clobbering) содержимого аппаратного регистра номер REGNO. "Таинственного" потому, что выражение insn RTL не описывает такого эффекта. Если эта макрокоманда не определена, это означает, что никакая insn не clobbers регистры. Это - обычная ситуация; при прочих равных условиях, выражению RTL лучше всего показать весь процесс. Если определена, то это - выражение C, значение которого отлично от нуля, если точные "REG_DEAD" замечания необходимы для номера аппаратного регистра REGNO во время получения ассемблерного кода. Когда это так, некоторые оптимизации, которые происходят после распределения регистров и могли бы свести на нет эти замечания, не выполняются, когда этот регистр включается в процесс. Вы могли бы принять меры, чтобы предотвратить "смертельную" информацию о регистре, когда некоторый код в машинном описании, который выполняется, чтобы писать ассемблерный код, видит "смертельные" замечания. Это необходимо только, когда фактическая аппаратная возможность, о которой GNU CC думает как о регистре, не является регистром обычного типа. (Это может быть, например, аппаратный стек.) Если эта макрокоманда не определена, это означает, что никакие "смертельные" примечания не должны сохраняться. Это - обычная ситуация. 6.6. Классы Регистров На многих машинах пронумерованные регистры - не все эквивалентны. Например, некоторые регистры нельзя использовать для индексной адресации; некоторые регистры нельзя использовать в некоторых командах. Эти машинные ограничения известны транслятору через "классы регистров". Вы определяете ряд классов регистров, придавая каждому имя и говоря, какие из регистров принадлежат ему. Затем вы можете задать классы регистров, которые разрешены как операнды в конкретных командах. Вообще, каждый регистр будет принадлежать нескольким классам. Фактически, один класс должен быть назван "ALL_REGS" ("все регистры") и содержать все регистры. Другой класс должен быть назван "NO_REGS" ("никаких регистров") и не содержать никаких регистров. Часто объединение двух классов будет другим классом, однако это необязательно. Один из классов должен быть назван "GENERAL_REGS" ("регистры общего назначения"). Имя не столь важно, но символы "r" и "g" определяет этот класс. Если "GENERAL_REGS' - то же, что "ALL_REGS", просто определите это как макрокоманду, которая разворачивается до "ALL_REGS". Упорядочьте классы так, чтобы, если класс X содержится в классе Y то X, имел меньший номер класса чем Y. Классы, отличные от "GENERAL_REGS", указываются в ограничениях на операнд через машинно-зависимые символы ограничения операнда. Вы можете определять такие символы, чтобы отвечать различным классам, затем использовать их в ограничениях на операнд. Вы должны определить класс для объединения двух классов всякий раз, когда некоторая команда позволяет использовать оба класса. Например, если команда позволяет использовать как регистр с плавающей точкой (сопроцессорный), так и регистр общего назначения для некоторого операнда, вы должны определить класс "FLOAT_OR_GENERAL_REGS" ("вещественные или общие регистры"), который включает оба из них. Иначе вы получите неоптимальный код. Вы должны также определить некоторую избыточную информацию относительно классов регистров: для каждого класса : какие классы он содержит и какие содержатся в нем; для каждой пары классов - самый большой класс, содержащийся в их объединении. Когда значение, занимающее несколько последовательных регистров ожидается в некотором классе, все используемые регистры должны принадлежать этому классу. Следовательно, классы регистров не могут использоваться для усиления требования к регистровой паре, чтобы начаться регистром с четным номером. Способ определять это требование - "HARD_REGNO_MODE_OK". Классы регистров, используемых как операнды побитового "И" или команд сдвига, имеют специальное требование: каждый такой класс должен иметь для каждого типа с фиксированной точкой, подкласс, регистры которого могут передать этот тип в память или из нее. Например, на некоторых машинах, операции для однобайтовых значений ("QImode") ограничены конкретными регистрами. Когда это так, каждый класс регистров, который используется в побитовом "И" , или командах сдвига, должен иметь подкласс, состоящий из регистров, из которых однобайтовые значения могут быть загружены или сохранены. Так что "PREFERRED_RELOAD_CLASS" всегда имеет значение для возврата. Перечислимый тип, который должен быть определен всеми именами классов регистров как перечисляемыми значениями. "NO_REGS" должен быть первым. "ALL_REGS" должен быть последним классом регистров, сопровождаемым еще одним значением, "LIM_REG_CLASSES", которое не является классом регистров, но сообщает о количестве классов. Каждый класс регистров имеет номер, который является значением приведения имени класса, к типу "int". Номер служит индексом во многих таблицах, описанных ниже. Число различных классов регистров, определенных следующим образом: #define N_REG_CLASSES (int) LIM_REG_CLASSES Инициализатор, содержащий имена классов регистров как строковые константы C. Эти имена используются при писании некоторых отладочных дампов. Инициализатор, содержащий оглавление классов регистров как целые числа, являющиеся битовыми масками. N-ое целое число определяет оглавление класса N. Целочисленная маска MASK интерпретируется так : регистр R находится в классе, если "MASK & (1 << R)" не равно нулю. Когда машина имеет больше чем 32 регистра, целого числа ("int") не хватает. Тогда целые числа заменяются под-инициализаторами, группами по несколько целых чисел. Каждый под-инициализатор должен подходить как инициализатор для типа "HARD_REG_SET", который определен в "hard-reg-set.h". Выражение C, значение которого - класс регистров, содержащий REGNO аппаратного регистра. В общем случае имеется более, чем один такой класс; выберите "минимальный" (такой что никакой меньший класс не содержит данный регистр). Макрокоманда, определение которой - имя класса которому должен принадлежать правильный базовый регистр . Базовый регистр - тот, который используется в адресе, являющемся значением этого регистра плюс смещение. Макрокоманда, определение которой - имя класса, которому должен принадлежать индексный регистр . Индексный регистр - тот, который используется в адресе, где значение его умножается на масштабирующий коэффициент или прибавляется к другому регистру (или добавляется к смещению). Выражение C, которое определяет машинно-зависимые символы ограничения операнда для классов регистров. Если CHAR - такой символ, значением должен быть класс регистров, соответствующий ему. Иначе, значение должно быть "NO_REGS". Символ регистра "r", соответствующий классу "GENERAL_REGS", не будет передан этой макрокоманде; вы не должны обрабатывать его. Выражение C, которое отлично от нуля, если регистр номер NUM подходит для использования как базовый регистр в адресах операндов. Это может быть как подходящий аппаратный регистр, так и псевдорегистр, в котором был распределен аппаратный регистр. Выражение C, которое отлично от нуля если регистр номер NUM подходит для использования как индексный регистр в адресах операндов. Это может быть как подходящий аппаратный регистр, так и псевдорегистр, в котором был распределен аппаратный регистр. Различие между индексным регистром и базовым регистром в том, что индексный регистр может масштабироваться. Если адрес включает сумму двух регистров, ни один из них не масштабирован, то любой может быть помечен как "основной", а другой как "индексный"; но какая бы пометка не использовалась, она должна удовлетворять ограничениям машины, регистры которой могут служить только в определенных местах. Транслятор пробует оба способа, ища правильный, и перезагружает один или оба регистра, только если никакой способ пометки не проходит. Выражение C, которое налагает дополнительные ограничения на класс регистров, если необходимо копировать значение X в регистр класса CLASS. Значение - класс регистров; возможно CLASS, или другой, меньший класс. На многих машинах, безопасно следующее определение : #define PREFERRED_RELOAD_CLASS (X, CLASS) CLASS Иногда возвращение более ограниченного класса приводит к лучшему коду. Например, на 68000, когда X - целочисленная константа, которая находится в диапазоне для команды "moveq", значение этой макрокоманды - всегда "DATA_REGS", так как CLASS включает регистры данных. Требование регистра данных гарантирует, что "moveq" будет использоваться. Если X - "const_double", возвращая "NO_REGS" вы может превратить X в константу в памяти. Это полезно на некоторых машинах, где непосредственные вещественные значения не могут быть загружены в некоторые типы регистров. То же, что и "PREFERRED_RELOAD_CLASS", но для перезагрузки вывода вместо перезагрузки ввода. Если Вы не определяете эту макрокоманду, по умолчанию используется CLASS, неизмененный. Выражение C, которое налагает дополнительные ограничения на класс регистров, чтобы использовать, когда необходимо держать значение типа MODE в перезагружаемом регистре для которого обычно использовался бы класс CLASS. В отличие от "PREFERRED_RELOAD_CLASS", эта макрокоманда должна использоваться, когда имеются некоторые типы, которые просто не могут входить в некоторые классы перезагрузки. Значение - класс регистров; возможно CLASS, или другой, меньший класс. Не определяйте эту макрокоманду, если целевая машина имеет ограничения, которые требуют, чтобы макрокоманда делала нечто нетривиальное. Многие машины имеют некоторые регистры, которые не могут быть скопированы непосредственно в память или из нее или даже из других типов регистров. Пример - "MQ" регистр, который на большинстве машин может быть скопирован только через регистр общего назначения, но не через память. Некоторые машины позволяют копировать все регистры в память или из нее , но требуют рабочего регистра для памяти (например, с символическим адресом на 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. X может быть псевдорегистром или подрегистром псевдорегистра, который может быть в аппаратном регистре или в памяти. Используйте "true_regnum", чтобы выяснить это; она возвратит -1, если псевдорегистр находится в памяти или номер аппаратного регистра, если псевдорегистр находится в регистре. Эти макрокоманды не должны использоваться в случае, если специфический класс регистров может быть только скопирован в память или в другой класс регистров. В этом случае, вторичные регистры перезагрузки необязательны и не помогают. Вместо этого для выполнения копирования должен использоваться стек, и образец "movM" должен использовать память как место промежуточного хранения. Это часто происходит между регистрами с плавающей точкой и регистрами общего назначения. Некоторые машины имеют свойство, что некоторые регистры не могут быть скопированы в некоторые другие регистры без использования памяти. Определите эту макрокоманду на этих машинах, чтобы это было выражением C, которое отлично от нуля, если объекты типа M в регистрах CLASS1 могут быть скопированы в регистры класса CLASS2, только сохраняя регистр CLASS1 в память и загружая ячейку памяти в регистр класса CLASS2. Не определяйте эту макрокоманду, если значение было бы всегда ноль. Обычно, когда "SECONDARY_MEMORY_NEEDED" определена, транслятор распределяет слот (slot) стека для ячейки памяти, необходимой для копии регистра. Если эта макрокоманда определена, то транслятор вместо этого использует ячейку памяти, определенную этой макрокомандой. Не определяйте эту макрокоманду, если Вы не определили "SECONDARY_MEMORY_NEEDED". Когда транслятор нуждается во вторичной ячейке памяти, чтобы копировать один регистр типа MODE в другой, он обычно распределяет достаточное количество памяти, чтобы хранить "BITS_PER_WORD" битов и выполняет операции сохранения/загрузки в типе шириной в много битов, класс которого - MODE. Это пойдет на большинстве машин, потому что это гарантирует, что все биты регистра скопированы, и предотвращает доступ к регистрам в более узкого (по ширине) типа, что некоторые машины запрещают для регистров с плавающей точкой. Однако, это заданное по умолчанию поведение неправильно на некоторых машинах, таких как, например, DEC Alpha, которые сохраняют короткие целых числа в регистрах с плавающей точкой не так, как в целочисленных регистрах. На этих машинах, заданное по умолчанию расширение не будет работать правильно, и вы должны определить эту макрокоманду, чтобы подавить это расширение в некоторых случаях. См. подробности в файле "alpha.h". Не определяйте эту макрокоманду, если Вы не определяете "SECONDARY_MEMORY_NEEDED" или если расширение MODE до типа, который имеет ширину "BITS_PER_WORD" бит, работает правильно на вашей машине. Обычно транслятор избегает выбирать регистры, которые были явно упомянуты в rtl, как spill-регистры (те, которые используются, для передачи параметров и возвращаемого значения). Однако, некоторые машины имеют слишком мало регистров некоторых классов, что не хватит регистров, чтобы использовать их как spill-регистры, если это было выполнено. Определите "SMALL_REGISTER_CLASSES" на этих машинах. Когда она определена, транслятор позволяет регистрам, явно используемым в rtl, использоваться как spill-регистры, но предотвращает расширение времени существования этих регистров. Определять эту макрокоманду всегда безопасно, но если вы без необходимости определяете ее, то в некоторых случаях вы уменьшите возможности оптимизации, которая может выполняться. Если вы не определяете эту макрокоманду, когда это требуется, что транслятор исчерпает все spill-регистры и напечатает сообщение о фатальной ошибке. На большинстве машин, вы не должны определять эту макрокоманду. Выражение C, значение которого отлично от нуля если псевдорегистры, которые были назначены регистрам класса CLASS, могут быть использованы как spill-регистры, потому что регистры класса CLASS необходимы для spill-регистров. Значение по умолчанию этой макрокоманды равно 1, если CLASS имеет ровно один регистр, иначе - ноль . На большинстве машин, это значение по умолчанию должно использоваться. Просто определите эту макрокоманду как некоторое другое выражение, если псевдорегистры, распределенные "local-alloc.c", заканчиваются в памяти, потому что их аппаратные регистры были необходимы для spill-регистров. Если эта макрокоманда возвращает не ноль для этих классов, эти псевдорегистры будут распределены "global.c", который знает, как перераспределить псевдорегистр в другой регистр. Если не имелось бы другого регистра для перераспределения, вам не следовало бы изменять определение этой макрокоманды, так как единственный эффект такого определения был бы замедление распределения регистров. Выражение C для максимального числа последовательных регистров класса CLASS, требуемых для хранения значения типа MODE. Макрокоманда тесно связана с макрокомандой "HARD_REGNO_NREGS". Фактически, значение макрокоманды "CLASS_MAX_NREGS (CLASS, MODE)" должно быть максимальным значением "HARD_REGNO_NREGS(REGNO,MODE)" по всем значениям REGNO в классе CLASS. Эти макрокоманды помогают управлять обработкой значений в много слов в процессе перезагрузки. Если определена, то это выражение C для класса, который содержит регистры, к которым транслятор должен всегда обращаться в типе, размер которого - тот же, что у типа, в котором он загрузил регистр. Для примера, загрузка 32-битного целого числа или числа с плавающей точкой в регистры с плавающей точкой на Альфе расширяет их до 64 битов. Следовательно, загрузка 64-битного объекта и затем сохранение его как 32-битного не сохраняют младшие 32 бита, как было бы в случае нормального регистра. Так что "alpha.h" определяет эту макрокоманду как "FLOAT_REGS". Три других специальных макрокоманды описывают, какие операнды пригодны для каких символов ограничения. Выражение C, которое определяет машинно-зависимые символы ограничения операнда которые определяют специфические диапазоны целочисленных значений. Если C - один из этих символов, выражение должно проверить, что VALUE, целое число, находится в соответствующем диапазоне и возвратить 1 если это так, иначе - 0. Если C - не является одним из этих символов, то значение должно быть 0 независимо от VALUE. Выражение C, которое определяет машинно-зависимые символы ограничения операнда, которые специфицируют конкретные диапазоны значений "const_double". Если C - один из этих символов, выражение должно проверить, что VALUE, RTX кода "const_double", находится в соответствующем диапазоне и возвратить 1 если так, иначе - 0. Если C - не является одним из этих символов, значение должно быть 0 независимо от VALUE. "const_double" используется для всех констант с плавающей точкой и для "DImode" констант с фиксированной точкой. Данный символ может принимать любое или оба вида значений. Он может использовать "GET_MODE " чтобы различить эти виды. Выражение C, которое определяет опциональные машинно-зависимые символы ограничения, которые могут использоваться, чтобы выделять специфические типы операндов, обычно ссылки в памяти, для целевой машины. Обычно эта макрокоманда не будет определена. Если это требуется для специфической целевой машины, она должна возвратить 1, если VALUE соответствует типу операнда, представленному символом ограничения C. Если C не определен как дополнительное ограничение, возвращенное значение должно быть 0 независимо от VALUE. Например, на ROMP, команды загрузки не могут иметь вывод в r0, если ссылка в памяти содержит символический адрес. Символ ограничения "Q" определен как представляющий адрес в памяти, который *не* содержит символического адреса. Альтернатива определена как "Q"-ограничение на ввод и "r"-ограничение на вывод. Следующая альтернатива определяет "m" на вводе и класс регистров, который не включает r0 на выводе. 6.7. Формат Стека и Соглашения о вызовах Здесь описываются формат стека и соглашения о вызовах. 6.7.1. 6.6.1.Основные Параметры Стека Вот основные параметры стека. Определите эту макрокоманду, если при вталкивании слова в стек указатель стека перемещается на меньший адрес. Когда мы говорим, "определите эту макрокоманду, если ...", это означает, что транслятор проверяет эту макрокоманду только с "#ifdef", так что точное определение не имеет значения. Определите эту макрокоманду, если адреса локальных переменных имеют отрицательные смещения относительно указателя кадра. Определите эту макрокоманду, если последовательные параметры к функции занимают уменьшающиеся адреса в стеке. Смещение от указателя кадра до первой локальной переменной, которая может быть распределена. Если "FRAME_GROWS_DOWNWARD", то смещение следующей локальной переменной слота находится вычитанием длины первой локальной переменной из "STARTING_FRAME_OFFSET". Или же прибавлением длины первой локальной переменной к значению "STARTING_FRAME_OFFSET". Смещение от регистра указателя стека до первого расположения, в которое помещены поступающие параметры. Если не определена, то используется значение по умолчанию - 0. Это - верное значение для большинства машин. Если "ARGS_GROW_DOWNWARD", то это - смещение к расположению над первым расположением, в которое помещены поступающие параметры. Смещение регистра-указателя параметров до адреса первого параметра. На некоторых машинах это может зависеть от типа функции. Если "ARGS_GROW_DOWNWARD", это - смещение расположения над адресом первого параметра. Смещение от регистра указателя стека до того, что динамически размещено в стеке, например "alloca". Значение по умолчанию для этой макрокоманды - "STACK_POINTER_OFFSET" плюс длина параметров. Значение по умолчанию правильно для большинства машин. См. подробности в "function.c". Выражение C, значение которого - RTL-представление адреса в кадре стека, где сохранен указатель на кадр вызывающей функции. Имейте в виду, что FRAMEADDR - RTL-выражение для адреса самого кадра стека. Если Вы не определяете эту макрокоманду, по умолчанию должно быть возвращено значение FRAMEADDR - то есть адрес кадра стека, или адрес слова стека, которое указывает на предыдущий кадр. Если определена, то это выражение C, которое производит машинно - специфический код установки стека в такое положение, чтобы можно было обращаться к произвольным кадрам. Например, на Sparc, мы должны сбросить все окна регистров в стек прежде, чем мы можем обращаться к произвольным кадрам. Эту макрокоманду редко надо определять. Выражение C, значение которого - RTL-представление значения адреса возврата для COUNT кадра, полученное из текущего кадра. FRAMEADDR - указатель COUNT кадра, или указатель на COUNT-1 кадра, если "RETURN_ADDR_IN_PREVIOUS_FRAME" определена. Определите ее, если к адресу возврата конкретного кадра стека обращаются из указателя предыдущего кадра стека. 6.7.2. Регистры, Адресующие Кадр стека Обсуждение регистров, адресующих кадр стека. Номер регистра указателя стека, который должен также быть фиксированным регистром из "FIXED_REGISTERS". На большинстве машин, аппаратные средства определяют, какой это регистр. Номер регистра указателя кадра, который используется, чтобы обратиться к автоматическим переменным в кадре стека. На некоторых машинах, аппаратные средства определяют, какой это регистр. На других машинах вы можете выбирать любой регистр, какой пожелаете, для этой цели. На некоторых машинах смещение между указателем кадра и начальным смещением автоматических переменных неизвестно, пока не выполнено распределение регистров (например, потому что сохраненные регистры - между этими двумя расположениями). На таких машинах определите "FRAME_POINTER_REGNUM" как номер специального, фиксированного регистра, который должен использоваться внутри (internally), пока смещение не станет известным, и определите "HARD_FRAME_POINTER_REGNUM" как фактический номер аппаратного регистра, используемого для указателя кадра. Вы должны определить эту макрокоманду только в очень редком случае, когда невозможно вычислить смещение между указателем кадра и автоматическими переменными, пока распределение регистров не было завершено. Когда эта макрокоманда определена, вы должны также указать в вашем определении "ELIMINABLE_REGS", как заменить "FRAME_POINTER_REGNUM" на "HARD_FRAME_POINTER_REGNUM" или на "STACK_POINTER_REGNUM". Не определяйте эту макрокоманду, если она совпадала бы с "FRAME_POINTER_REGNUM". Номер arg регистра указателя, который используется для обращения к списку параметров функции. На некоторых машинах, это - то же, что и регистр указателя кадра. На некоторых машинах, аппаратные средства определяют, какой это регистр. На других машинах вы можете выбрать любой регистр, который пожелаете, для этой цели. Если это - не тот же самый регистр, что и регистр указателя кадра, то Вы должны отметить его как фиксированный регистр из "FIXED_REGISTERS", или принять меры для устранения его (*обратите внимание на Удаление::.). Номера регистров, используемые для передачи указателя статической цепи функции. Если используются окна регистров, то это номер регистра, видимый вызываемой функцией - "STATIC_CHAIN_INCOMING_REGNUM", в то время как номер регистра, видимый вызванной функцией - "STATIC_CHAIN_REGNUM". Если эти регистры одни и те же, то "STATIC_CHAIN_INCOMING_REGNUM" не должна быть определена. Статический регистр цепи не должен быть фиксированным регистром. Если статическая цепочка передана в память, эти макрокоманды не должны быть определены; вместо этого должны быть определены следующие две макрокоманды. Если статическая цепочка передана в память, эти макрокоманды обеспечивают rtx данных выражений "mem", которые обозначают, где они сохранены. "STATIC_CHAIN" и "STATIC_CHAIN_INCOMING" делают ячейки видимыми как вызывающим, так и вызываемым функциям, соответственно. Часто первая будет при смещении из указателя стека и последняя при смещении из указателя кадра. Переменные "stack_pointer_rtx", "frame_pointer_rtx" и "arg_pointer_rtx" будут проинициализированы до использования этих макрокоманд и должны использоваться для обращения к этим структурам. Если статическая цепочка передана в регистр, две предыдущих макрокоманды должны быть определены вместо этих. 6.7.3. Удаление Указателя Кадра и Указателя Аргументов Что касается удаления указателя кадра и указателя аргументов. Выражение C, которое отлично от нуля, если функция имеет и использует указатель кадра. Это выражение вычисляется в процессе перезагрузки. Если значение отлично от нуля, функция будет иметь указатель кадра. Это выражение может в принципе исследовать текущую функцию и решить, опираясь на факты, но на большинстве машин, достаточно констант 0 и 1. Используйте 0, когда машина позволяет коду быть сгенерированному без указателя кадра, и это сохраняет немного времени и места. Используйте 1, когда нет преимущества от отсутствия указателя кадра. В некоторых случаях, транслятор не знает, как произвести правильный код без указателя кадра. Транслятор распознает такие случаи и автоматически делает указатель кадра для функции, независимо от значения "FRAME_POINTER_REQUIRED". Не надо беспокоиться о них. В функции, которой не требуется указатель кадра, регистр указателя кадра может быть распределен для обычного использования, если Вы не отметили его как фиксированный регистр. Для дополнительной информации об этом ищите "FIXED_REGISTERS". Оператор C сохранения в переменной DEPTH-VAR разности между указателем кадра и указателем стека сразу после пролога функции. Значение было бы вычислено из информации типа результата "get_frame_size()" и таблиц регистров "regs_ever_live" и "call_used_regs". Если "ELIMINABLE_REGS" определена, эта макрокоманда не будет использоваться и не должна быть определена. Иначе она должна быть определена, даже если "FRAME_POINTER_REQUIRED" определена как тождественно истинный; в этом случае, вы можете присвоить DEPTH-VAR любое значение. Если определена, эта макрокоманда определяет таблицу пар регистров, используемую для устранения ненужных регистров, которые указывают на кадр стека. Если не определена, единственное удаление, которое транслятор пытается сделать - замена ссылок на указатель кадра на ссылки на указатель стека. Определение этой макрокоманды состоит в списке инициализаций структур, каждая из которых определяет оригинальный регистр и регистр замены. На некоторых машинах позиция указателя параметров неизвестна, пока трансляция не завершена. В таком случае, отдельный аппаратный регистр должен использоваться как указатель параметров. Этот регистр может быть удален путем замены на указатель кадра или на указатель параметров, в зависимости от того, удален или не удален указатель кадра. В этом случае, вы могли бы определить: #define ELIMINABLE_REGS \ {{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} Обратите внимание, что удаление указателя параметров с указателем стека определено сначала, так как это удаление предпочтительно. Выражение C, которое возвращает значение, отличное от нуля, если транслятору позволяется пробовать заменять регистр номер FROM- REG на регистр номер TO-REG. Эта макрокоманда должна быть определена только если определена "ELIMINABLE_REGS", и будет обычно константной 1, так как большинство случаев, предотвращающих удаление регистра - вещи о которых транслятор уже знает. Эта макрокоманда подобна "INITIAL_FRAME_POINTER_OFFSET". Она определяет начальное различие между парой регистров. Макрокоманда должна быть определена, если "ELIMINABLE_REGS" определена. Определяйте эту макрокоманду, если функция "longjmp" восстанавливает регистры из кадра стека, а не из сохраненных "setjmp". Некоторые величины не должны сохраняться в регистрах во время обращения к "setjmp" на таких машинах. 6.7.4. Передача Аргументов Функции через Стек Макрокоманды в этом разделе управляют тем, как параметры передаются в стек. См. следующий раздел о других макрокомандах, которые управляют передачей конкретных типов параметров через регистры. Определяйте эту макрокоманду, если параметр, объявленный в прототипе как целый тип меньший, чем "int", должен фактически быть передан как "int". В дополнение к избежанию ошибок в некоторых случаях несоответствия, это также приводит к лучшему коду на некоторых машинах. Выражение C, которое является числом байт, фактически переданных через стек, когда команда пытается помещать NPUSHED байт. Если целевая машина не имеет команды push ("поместить в стек"), не определяйте эту макрокоманду. Это указывает GNU CC использовать альтернативную стратегию: распределить весь блок параметров и затем сохранить параметры в стеке. На некоторых машинах, определение #define PUSH_ROUNDING (BYTES) (BYTES) будет в самый раз. Но на других машинах команды, которые помещают в стек один байт, фактически помещает два байта, поддерживая выравнивание. Тогда определение должно быть : #define PUSH_ROUNDING (BYTES) (((BYTES) + 1) и ~1) Если определена, то максимальный размер области, требуемой для параметров, будет вычислен и будет помещен в переменную "current_function_outgoing_args_size". Ничего не будет помещаться на стек при каждом вызове; вместо этого пролог функции должен увеличить размер кадра стека на это количество байт. Нельзя определять "PUSH_ROUNDING" и "ACCUMULATE_OUTGOING_ARGS" одновременно. Определите эту макрокоманду, если функции рассчитывают, что место в стеке распределено для параметров даже когда их значения переданы в регистрах. Значение этой макрокоманды - размер в байтах области, зарезервированной для параметров, переданных в регистрах функции, представляемой FNDECL. Это место может быть распределено вызывающей функцией, или быть частью машинно-зависимого кадра стека: "OUTGOING_REG_PARM_STACK_SPACE" говорит, кто его распределит. Определите эти макрокоманды в дополнение к описанной выше, если функции могут распределять место в стеке для параметров даже когда их значения переданы через регистры. Они должны использоваться, когда место в стеке, распределенное для параметров в регистрах - не простая константа, независимая от декларации функции. Значение первой макрокоманды - размер в байтах области, которую мы должны первоначально считать зарезервированной для параметров, переданных в регистрах. Значение второй макрокоманды - фактический размер в байтах области, которая будет зарезервирована для параметров, переданных в регистрах. У нее два параметра: целое число, представляющее число байт параметров фиксированного размера, и дерево, представляющее число байт параметров переменного размера, переданных в стек. Когда эти макрокоманды определены, "REG_PARM_STACK_SPACE" будет вызываться только для функций libcall, текущей функции и для функции, вызываемой, когда о ней известно, что такое место в стека должно быть распределено. В любом случае это значение может быть легко вычислено. При выяснении, нуждается ли вызываемая функция в таком пространстве стека, и сколько надо резервировать, GNU CC использует эти две макрокоманды вместо "REG_PARM_STACK_SPACE". Определите ее, если вызывающая функция обязана распределить область, зарезервированную для параметров, переданных в регистрах. Если "ACCUMULATE_OUTGOING_ARGS" определена, эта макрокоманда управляет, не превосходит ли место для параметров значение "current_function_outgoing_args_size". Определите эту макрокоманду, если "REG_PARM_STACK_SPACE" определена, но стековые параметры не пропускают область, определенную ею. Обычно, когда параметр не передан в регистрах, он помещен в стек за "REG_PARM_STACK_SPACE" областью. Определение этой макрокоманды подавляет такое поведение и заставляет параметр передаваться в стек на естественное место. Выражение C, которое должно указать размер в байтах параметров функции, которые она выталкивает при возврате, или 0, если функция не выталкивает параметры, а вызывающая функция должна следовательно вытолкнуть их после возврата из функции. FUNDECL - переменная C, значение которой - узел дерева, который описывает рассматриваемую функцию. Обычно это - узел типа "FUNCTION_DECL", который описывает декларацию функции. Из нее можно получить DECL_MACHINE_ATTRIBUTES для функции. FUNTYPE - переменная C, значение которой - узел дерева, который описывает рассматриваемую функцию. Обычно это - узел типа "FUNCTION_TYPE", который описывает тип функции. Из нее можно получить типы значения и параметров (если они есть). Когда рассматривается обращение к библиотечной функции, FUNTYPE будет содержать идентификатор узла для библиотечной функции. Таким образом, если нужно различить библиотечные функции, вы можете сделать это по их именам. Обратите внимание, что "библиотечная функция" в этом контексте означает арифметическую функцию, имя которой известно только транслятору и не упомянуто в компилируемом C-коде. STACK-SIZE - число байтов параметров, переданных в стек. Если передано переменное число байт, это - ноль, и выталкивание параметров всегда будет обязанностью вызывающей функции. На Vax все функции всегда выталкивают свои аргументы, так что определение этой макрокоманды - STACK-SIZE. На 68000, согласно стандартному соглашению о вызовах, никакие функции не выталкивают свои параметры, так что значение макрокоманды - всегда 0 в этом случае. Но альтернативное соглашение о вызовах имеет место, если функции, которые берут фиксированное число параметров, выталкивают их, но другие функции (типа "printf") не выталкивают ничего (вызывающая функция делает это за них). Когда используется это соглашение, исследуется FUNTYPE для определения, берет ли функция фиксированное число аргументов. 6.7.5. Передача Параметров В Регистрах Этот раздел описывает макрокоманды, которые позволяют Вам управлять тем, как различные типы параметров передаются в регистрах или как они размещаются в стеке. Выражение C, которые управлял тем, передается ли параметр функции в регистре, и в каком регистре. Параметры - CUM, который суммирует все предыдущие параметры; MODE, машинный тип параметра; TYPE, тип данных параметра как узел дерева или 0, если он не известен (как бывает для библиотечных функций поддержки C); и NAMED, который равен 1 для обычного парамета и 0 для безымянных параметров, что соответствет " ... " в прототипе вызываемой функции. Значение выражения должно быть или " 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_ARG " вычисляет регистр, в котором вызывающая функция передает значение, а " FUNCTION_INCOMING_ARG " должен определяться подобным способом и сообщать вызываемой функции, где получать параметры. Если " FUNCTION_INCOMING_ARG " не определено, " FUNCTION_ARG " служит для обеих целей. Выражение C для числа слов, в начале параметра, которые должны быть помещены в регистры. Значение должно быть нулем для параметров, которые полностью передаются в регистрах или полностью помещаются в стек. На некоторых машинах определенные параметры должны передаваться частично в регистрах и частично в памяти. На этих машинах обычно первые N слов параметров передаются в регистрах, а остальные - в стеке. Если параметр, состоящий из нескольких слов (" double " или структура), пересекает эту границу, его первые несколько слов должны передаваться в регистрах, а остальные - в стеке. Эта макрокоманда сообщает транслятору, когда это происходит и сколько слов должен идти в регистры. " FUNCTION_ARG " для этих параметров должна возвращать первый регистр, который должен использоваться вызывающей функцией для этого параметра; аналогично " FUNCTION_INCOMING_ARG " - для вызываемой функции. Выражение C, которое указывает, когда параметр должен передаваться по ссылке. Если оно отлично от нуля для параметра, в памяти делается копия этого параметра и указатель на параметр передается вместо него самого. Указатель передается любым способом, соответствующий передаче указателя на этот тип. На машинах, где " REG_PARM_STACK_SPACE " не определено, определения этой макрокоманды может выглядеть так: #define FUNCTION_ARG_PASS_BY_REFERENCE\ (CUM, MODE, TYPE, NAMED) \ MUST_PASS_IN_STACK (MODE, TYPE) Если определено, выражение C, которое указывает когда задача создания копий параметров, переданных по невидимой ссылке, возлагается на вызываемую функцию. Обычно вызывающая функция делает копию и передает ее адрес вызываемой подпрограмме. Когда FUNCTION_ARG_CALLEE_COPIES определена и отлична от нуля, вызывающая функция не делает копии. Вместо этого она передает указатель на "настоящее" значение. Вызываемая функция не должна изменять это значение. Если можно определить, будет ли значение изменяться, делать копию не обязательно; иначе копия должна быть сделана. Тип C для объявления переменной, которая используется как первый параметр " FUNCTION_ARG " и другие связанных значений. Для некоторых целевых машин тип " int " подходит для этой цели и может хранить количество байтов. Нет необходимости записывать в " CUMULATIVE_ARGS " что-либо о параметрах, которые передаются через стек. Транслятор другие переменные, чтобы следить за этим. Для целевых машин, на которых все параметры передаются через стек, нет необходимости записывать что-либо в " CUMULATIVE_ARGS "; однако, структура данных должна существовать и не должна быть пустой, так что используйте " int ". Оператор C (без точки с запятой) для инициализации переменной CUM для состояния в начале списка параметров. Переменная имеет тип " CUMULATIVE_ARGS ". Значение FNTYPE - узел дерева для типа данных функции, которая получит параметры, или 0 если аргументы - для библиотечной функции поддержки транслятора. При обработке обращения к библиотечной функции поддержки транслятора, LIBNAME определяет, к какой именно. Это - " symbol_ref " rtx, который содержит имя функции как строку. LIBNAME равно 0, если обрабатывается обычное обращение к функции C. Таким образом, каждый раз эта макрокоманда вызывается, или LIBNAME, или FNTYPE отличны от нуля, но не оба сразу. Подобно " INIT_CUMULATIVE_ARGS ", но перекрывает ее для целей нахождения параметров для компилируемой функции. Если это макрокоманда не определена, используется " INIT_CUMULATIVE_ARGS ". Значение, передаваемое для LIBNAME - всегда 0, потому что библиотечные подпрограммы со специальными соглашениями о вызовах никогда не компилируются GNU CC. Параметр LIBNAME существует для симметрии с " INIT_CUMULATIVE_ARGS ". Оператор C (без точки с запятой) для продвижения сумматора переменной CUM мимо параметра в списке параметров. Значения MODE, TYPE и NAMED описывают этот параметр. Как только это выполнено, переменная CUM подходит для анализа *следующего* параметра при помощи " FUNCTION_ARG ", и т.д. Эта макро не должно ничего делать, если рассматриваемый параметр передается в стеке. Транслятор знает, как отслеживать количество свободного места в стеке, используемого для параметров, без специальной помощи. Если определено, выражение C, которое определяет, добавлять ли, и в каком направлении дополнительное пространство параметру. Значение должно иметь тип " enum direction ": " upward ", чтобы дополнить сверху, " downward ", чтобы дополнить снизу, или " none ", чтобы запретить дополнение. *Размера* дополнения всегда в точночти достаточно, чтобы достигнуть следующего кратного " FUNCTION_ARG_BOUNDARY "; эта макрокоманда не управляет этим. Эта макрокоманда имеет заданное по умолчанию определение, которое является правильным для большинства систем. Для little- endian машин значение по умолчанию - вверх. Для big-endian машин - вниз для параметра постоянного размера, короче чем " int ", и вверх в противном случае. Если определено, выражение C, которое дает границу выравнивания, в битах, параметра с указанными типом и типом. Если оно не определено, для всех параметров используется " PARM_BOUNDARY ". Выражение C, которое является отличным от нуля если REGNO - номер аппаратного регистра, в котором иногда передаются параметры функций. Это *не включает* неявные параметры типа статической цепочки и адрес значения структуры. На многих машинах никакие регистры не могут использоваться для этой цели, так как все параметры функций передаются через стек. 6.7.6. Как Возвращаются Значения Скалярной Функции В этом разделе обсуждаются макрокоманды, которые управляют возвращением скаляров как значений - значений, которые помещаются в регистрах. Определите эту макрокоманду, если " -traditional " не должно заставлять функции, объявленные возвращающими " float ", преобразовывать значение в " double ". Выражение C для создания RTX представления места, где a функция возвращает значение типа данных VALTYPE. VALTYPE - узел дерева, представляющий тип данных. Пишите " TYPE_MODE (VALTYPE) ", чтобы получить машинный тип, используемый, чтобы представить этот тип. На многих машинах, уместен только тип. (Фактически, на большинстве машин, скалярные значения возвращаются в одном и том же месте независимо от типа). Если " PROMOTE_FUNCTION_RETURN " определено, Вы должны применить те же правила поддержки, что и определенные в " PROMOTE_MODE ", если VALTYPE - скалярный тип. Если точная вызываемая функция известна, FUNC - узел дерева (" FUNCTION_DECL ") для нее; иначе, FUNC - нулевой указатель. Это делает возможным использовать различные соглашения о возвращении значений для отдельных функций, когда известны все обращения к ним. " FUNCTION_VALUE " не используется для возврата значений составных типов данных, потому что они возвращаются другим способом. См. ниже " STRUCT_VALUE_REGNUM " и связанные с ним макрокоманды. Определите эту макрокоманду, если целевая машина имеет " окна регистра ", то есть регистр, в котором функция видит параметры, не обязательно является тем, в котором вызывающая функция их передала. Для таких машин " FUNCTION_VALUE " вычисляет регистр, в котором вызывающая функция получит значение, а " FUNCTION_INCOMING_ARG " должен определяться подобным способом и сообщать функции, куда записать параметры. Если " FUNCTION_OUTGOING_VALUE " не определена, " FUNCTION_VALUE " служит для обеих целей. " FUNCTION_OUTGOING_VALUE " не используется для возврата значений составных типов данных, потому что они возвращаются другим способом. См. ниже " STRUCT_VALUE_REGNUM " и связанные с ним макрокоманды. Выражение C, чтобы создать RTX представление места, где библиотечная функция возвращает значение типа MODE. Если точная вызываемая функция известна, FUNC - узел дерева (" FUNCTION_DECL ") для нее; иначе, FUNC - нулевой указатель. Это делает возможным использовать различные соглашения о возвращении значений для отдельных функций, когда известны все обращения к ним. Обратите внимание, что "библиотечная функция" в этом контексте означает подпрограмму поддержки транслятора, используемую для выполнения арифметической операции, чье имя специально указано транслятору и не было упомянуто в компилируемом C коде. Определение " LIBRARY_VALUE " не должно касаться составных типов данных, потому что ни одна из библиотечных функций не возвращает таких типов. Выражение C, которое является отличным от нуля, если REGNO - номер аппаратного регистра, в котором могут возвращаться значения вызываемой функции. Регистр, чей использование для возврата значений ограничено употреблением как второго в пары (для значения типа " double ", например), не требуется распознавать этой макрокомандой. Поэтому для большинства машин, достаточно такого определения: #define FUNCTION_VALUE_REGNO_P(N) ((N) == 0) Если машина имеет окна регистра, так, чтобы вызывающая и вызываемая функции использует различные регистры для возврата значения, эта макрокоманда должна распознать только номера регистров вызывающей функции. Определите эту макрокоманду, если " untyped_call " и " untyped_return " требуется больше места, чем подразумевается " FUNCTION_VALUE_REGNO_P ", для сохранения и восстановления произвольного возвращаемого значения. 6.7.7. Как Возвращаются Структурированные Значения Когда тип значения функции - " BLKmode " (и в некоторых других случаях), значение не возвращается согласно " FUNCTION_VALUE " (*См.:Возврат скаляров::.). Вместо этого вызывающая функция передает адрес блока памяти, в котором значение должно быть сохранено. Этот адрес называется " структурным адресом значения ". Этот раздел описывает, как управлять возвратом значений структур в памяти. Выражение C, которое может запрещать возврат определенных значений функции в регистрах, основываясь на типе значения. Ненулевое значение требует возврата значения функции в памяти, так, как это делается для больших структур. Здесь TYPE будет выражение C типа " tree ", представляя тип данных значения. Обратите внимание, что значения типа " BLKmode " должны явно обрабатываться этой макрокомандой. Также, опция " -fpcc-struct- return " имеет эффект независимо от этой макрокоманды. На большинстве систем можно оставить макрокоманду неопределенной; тогда используется определение по умолчанию, чье значение - константа 1 для значений ` BLKmode ' и 0 в противном случае. Не используйте эту макрокоманду, чтобы указать, что структуры и объединения всегда должны возвращаться в памяти. Вы должны использовать " DEFAULT_PCC_STRUCT_RETURN ", чтобы указать это. Определите эту макрокоманду значением 1, если все возвращаемые значения структур и объединений должны быть в памяти. Так как это приводит к более медленному коду, это должно делаться, только если это необходимо для совместимости с другими трансляторами или с ABI. Если Вы определяете эту макрокоманду значением 0, то соглашения, используемые для возвращаемых значений структур и объединений, определяются макрокомандой " RETURN_IN_MEMORY ". По умолчанию значение равно 1. Если адрес значения структуры передается в регистре, то " STRUCT_VALUE_REGNUM " должно быть номером этого регистра. Если адрес значения структуры не передается в регистре, определите " STRUCT_VALUE " как выражение, возвращающее RTX для места, где адрес передается. Если возвращается 0, адрес передается как "невидимый" первый параметр. При некоторых архитектурах - место, где вызываемая функция находит адрес значения структуры - не то же самое место, куда его помещает вызывающая. Это может быть из-за регистровых окон или из-за того, что пролог функции перемещает его в другое место. Если входящее расположение адреса значения структуры находится в регистре, определите эту макрокоманду как номер регистра. Если входящее расположение - не регистр, то Вы должны определить " STRUCT_VALUE_INCOMING " как выражение для RTX, содержащее место, в котором вызываемая функция должна найти значение. Если она должна найти его в стеке, определите ее как " mem ", которое ссылается на указатель кадра. Определение нулем означает, что адрес передается как "невидимый" первый параметр. Определите эту макрокоманду, если обычное системное соглашение на целевой машине для возврата структур и объединений заключается в том, что вызываемая функция должна возвращать адрес статической переменной, содержащей значение. Не определите ее, если обычное системное соглашение системы заключается в том, что вызывающая функция передает адрес подпрограмме. Эта макрокоманда имеет эффект в типе " -fpcc-struct-return " , но не играет роли, когда Вы используете тип " -freg-struct- return ". 6.7.8. Распределение Регистров с Сохранением Если Вы разрешаете это, GNU CC может сохранять регистры на время обращений к функции. Это делает возможным использование затираемых регистров для содержания переменных, которые должны существовать и до, и после обращения. Определите эту макрокоманду, если обращения к функции на целевой машине не сохраняют никаких регистров; другими словами, если " CALL_USED_REGISTERS " имеет 1 для всех регистров. Эта макрокоманда допускает " -fcaller-saves " по умолчанию. В конечном счете эта опция будет установлена по умолчанию на всех машинах и как опция, так и эта макрокоманда будут удалены. Выражение C, которое определяет, имеет ли смысл рассмотреть размещение псевдорегистра в затираемом аппаратном регистре и сохранять и восстанавливать его при каждом обращении к функции. Выражение должно быть равно 1, если так следует поступать, и 0 - если нет. Если Вы не определяете эту макрокоманду, используется значение по умолчанию, который является разумным для большинства машин: " 4 * CALLS < REFS ". 6.7.9. Вход и Выход из Функции Этот раздел описывает макрокоманды, которые выводят код входа функции ("пролог") и выхода ("эпилог"). Составной оператор C, который выводит код ассемблера для входа функции. Пролог ответственен за установку кадра стека, инициализацию регистра указателя кадра, сохранение регистров, которые должны быть сохранены и распределение SIZE дополнительных байтов в памяти для локальных переменных. SIZE - целое число. FILE - stdio поток (поток стандартного ввода-вывода), в который должен выводиться код ассемблера. Метка для начала функции не должна выводиться этой макрокомандой. Это выполняется при запуске макрокоманды. Чтобы определить, какие регистры сохранять, макрокоманда могут обратиться к массиву " regs_ever_live ": элемент R отличен от нуля, если аппаратный регистрир R используется где-нибудь внутри функции. Это подразумевает, что пролог функции должен сохранить регистр R, если это - не один из используемых обращением регистры. (" FUNCTION_EPILOGUE " должен аналогично использовать " regs_ever_live ".) На машинах, которые имеют " окна регистра ", код входа функции не сохраняет в стеке регистры, которые находятся в окнах, даже если предполагается, что они должны сохраняться при обращении к функции; вместо этого требуется соответствующие шаги, чтобы "поместить" в стек регистр, если какие-то регистры, не используемые в вызове, используются в функции. На машинах, где функции могут иметь или могут не иметь указатели кадра, код входа функции должен изменяться соответственно; он должно устанавливать указатель кадра, если он требуется, и не устанавливать в противном случае. Чтобы определить нужен ли указатель кадра, макрокоманда может обратиться к переменной " frame_pointer_needed ". Значение переменной будет 1 во время выполнения фнукции, которая нуждается в указателе кадра. * См.: Удаление::. Код входа функции ответственен за распределение любого места в стеке, требуемого для функции. Этот пространство состоит из областей, перечисленных ниже. В большинстве случаев эти области, распределяются в перечисленном порядке, причем названная последней область - самой близкой к вершине стека (наименьший адрес, если " STACK_GROWS_DOWNWARD " определено, и наибольший адрес, если не определено). Вы можете использовать другой порядок для машины, если это будет удобнее или требуется по причинам совместимости. За исключением случаев, когда это требуется стандартом или отладчиком, не имеется никакой причины считать, что распределение стека, используемое GCC, должно согласовываться с используемым другими трансляторами для машины. ╥ Область из " current_function_pretend_args_size " байтов неинициализированного места под первым параметром, помещаемым в стек. (Оно не может быть в самом начале распределяемой области стека, если последовательность вызовов поместила в стек что-нибудь еще, кроме с параметров стека. Но обычно на таких машинах ничто больше не помещается, потому что пролог функции делает это сам.) Эта область используется на машинах, где параметр может передаваться частично в регистрах и частично в памяти, и, в некоторых случаях, чтобы поддерживать особенности " varargs.h " и " stdargs.h ". ╥ Область памяти, используемая для сохранения некоторых регистров, используемых функцией. Размер этой области, которая может также включать место для таких вещей, как адрес возврата и указателей предыдущие кадры стека, является машинно-специфическими и обычно зависит от того, какие регистры используются в функции. Машины с окнами регистра часто не требуют области сохранения. ╥ Область по крайней мере SIZE байтов, возможно округленных до границы распределения, для содержания локальных переменных функции. На некоторых машинах эта область и область сохранения могут располагаться в противоположном порядке, так что область сохранения будет ближе к вершине стека. ╥ Опционально, когда " ACCUMULATE_OUTGOING_ARGS " определено, область из " current_function_outgoing_args_size " байтов для использования для выходящих списков параметров функции. * См.: Параметры Стека::. Обычно, это необходимо для макрокоманд " FUNCTION_PROLOGUE " и " FUNCTION_EPILOGUE ", особенно чтобы обработать функции листа. Переменная C " leaf_function " отлична от нуля для такой функции. Определите эту макрокоманду как выражение C, которое отлично от нуля, если команда возврата или эпилог функции игнорирует значение указателя стека; другими словами, если безопасно удалить команду корректировки указателя стека перед возвратом из функции. Обратите внимание, что значение этой макрокоманды имеет значение только для функций, которые поддерживают указатели кадра. Не бывает безопасно удалить конечную корректировка стека в функции, которая не имеет указателя кадра, и транслятор знает это независимо от " EXIT_IGNORE_STACK ". Составной оператор C, который выводит код ассемблера для выхода из функции. Эпилог ответственен за восстановление сохраненных регистров и указателя стека на их значения, когда функция вызывалась, и возвращения управления вызывающей функции. Эта макрокоманда имеет те же параметры, что и макрокоманда " FUNCTION_PROLOGUE ", и регистры, которые нужно восстановить, определяются из " regs_ever_live " и " CALL_USED_REGISTERS " таким же образом. На некоторых машинах имеется одиночная команда, которая делает всю работу для возврата из функции. На этих машинах, задайте этой команде имя " return " и не определяйте макрокоманду " FUNCTION_EPILOGUE " вообще. Не определяйте образец по имени " return ", если Вы хотите использовать " FUNCTION_EPILOGUE ". Если Вы хотите, чтобы целевые переключатели управляли тем, используются команды возврата или эпилоги, определите образец " return " с условием проверки правильности, которое соответственно проверяет целевые переключатели. Если условие правильности образца " return " ложно, будут использоваться эпилоги. На машинах, где функции могут иметь или могут не иметь указатели кадра, код завершения функции должен изменяться соответственно. Иногда код для этих двух случаев полностью различен. Чтобы определить, требуется ли указатель кадра, макрокоманда может обратиться к переменной " frame_pointer_needed ". Значение переменной равно 1, если компилируется функция, которая нуждается в указателе кадра. Обычно, " FUNCTION_PROLOGUE " и " FUNCTION_EPILOGUE " должен обрабатывать функции листа специальным образом. Переменная C " leaf_function " отлична от нуля для такой функции. * См.: Функции Листа::. На некоторых машинах некоторые функции восстанавливают из стека их параметры на выходе, в то время как другие оставляют делать это вызывающей функции. Например, 68020, когда задано " -mrtd ", восстанавливает из стека параметры в функциях, которые имеют фиксированное число параметров. Ваше определение макрокоманды " RETURN_POPS_ARGS " решает, какие функции восстанавливают из стека их собственные параметры. " FUNCTION_EPILOGUE " требуется знать, что было решено. Переменная, которая называется " current_function_pops_args " - число байтов параметров, которые функция должна восстанавливать из стека. * См.: Возвращение Скаляров::. Определите эту макрокоманду, если эпилог функции содержит слоты задержки, в которые команды из остальной части функции могут "перемещаться". Определение должно быть выражением C, чье значение - целое число, представляющее число слотов задержки. Выражение C, которое возвращает 1, если INSN может быть помещен в слот задержки номер N эпилога. Параметр N - целое число, которое идентифицирует рассматриваемый слот задержки (так как различные слоты могут иметь различные правила "подходящести"). Это всегда неотрицателен и всегда меньше, чем количество слотов задержки эпилога (которое возвращает " 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.7.10. Генерация Кода для Профилирования Эти макрокоманды помогут Вам генерировать код для профилирования. Оператор C или составной оператор для вывода в FILE некоторого кода ассемблера, вызывающего подпрограмму профилирования " mcount ". Перед вызовом код ассемблера должен загрузить адрес переменной-счетчика в регистр, где " mcount " ожидает адрес. Имя этой переменной " LP " сопровождается номером LABELNO, так что Вам следует генерировать имя, используюя " LP%d " в " fprintf ". Подробности того, как адрес должен передаваться " mcount ", определяются вашей средой операционной системы, а не GNU CC. Чтобы представить их себе, скомпилируйте маленькую программу для профилирования, используя установленный C транслятор системы и посмотрите не получившийся код ассемблера. Определите эту макрокоманду, если код для профилирования функции должен идти перед прологом функции. Обычно код профилирования идет после него. Оператор C или составной оператор для вывода в FILE некоторого кода ассемблера, инициализирующего профилирование базового блока для текущего объектнго модуля. Этот код должен вызывать подпрограмму " __ bb_init_func " по одному разу для каждого объектного модуля, передавая ей к качестве единственного параметра адрес блока, распределенного в объектном модуле. Имя блока - локальный символ, созданный этим оператором: ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0); Конечно, так как Вы записываете определение " ASM_GENERATE_INTER╜ NAL_LABEL " так же, как для этой макрокоманды, Вы можете сделать вырезку из определения этой макрокоманды и использовать имя, которое, как Вы знаете, даст результат. Первое слово этого блока - флаг, который будет отличный от нуля, если объектный модуль уже был инициализирован. Поэтому проверяйте вначале это слово и не вызывайте " __ bb_init_func ", если флаг отличен от нуля. Оператор C или составной оператор для увеличения счетчика, связанного с базовым номером блока BLOCKNO. Базовые блоки нумеруются отдельно от нуля внутри каждой трансляции. Счетчик, связанный с номером блока BLOCKNO - это индекс BLOCKNO в векторе слов; имя этого массива - локальный символ, сделанный создаваемый таким оператором: ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 2); Конечно, так как Вы записываете определение " ASM_GENERATE_INTER╜ NAL_LABEL " так же, как для этой макрокоманды, Вы можете сделать вырезку из определения этой макрокоманды и использовать имя, которое, как Вы знаете, даст результат. Функция или функции C, которые необходимы в библиотеке для поддержки блока профилирования. 6.8. Выполнение Varargs Макрокоманд GNU CC имеет встроенную реализацию " varargs.h " и " stdarg.h ", которая работает без изменения на машинах, которые передают параметры через стек. Другие машины требуют собственных реализаций varargs, и два машиннонезависимых файла заголовка должны иметь условные выражения, чтобы включить их. ANSI " stdarg.h " отличается от традиционного " varargs.h " главным образом соглашением о вызовах для " va_start ". Традиционная реализация имеет только один параметр, который является переменной для сохранения указателя на параметр. ANSI реализация " va_start " имеет дополнительный второй параметр. Предполагается, что пользователь пишет здесь последний именованный параметр функции. Однако, " va_start " не должна использовать этот параметр. Способ нахождения окончания именованных параметров встроенными функциями описан ниже. Используйте эту встроенную функцию, чтобы сохранить параметры из регистров в памяти так, чтобы varargs механизм мог обращаться к ним. И ANSI, и традиционная версии " va_start " должны использовать " __ builtin_saveregs ", если Вы не используете вместо этого " SETUP_INCOMING_VARARGS " (см. ниже). На некоторых машинах " __ builtin_saveregs " открыта закодировано под контролем над макрокомандой " EXPAND_BUILTIN_SAVEREGS ". На других машинах она вызывает подпрограмму, написанную на языке ассемблеров, находящуюся в " libgcc2.c ". Код, сгенерированный для обращения к " __ builtin_saveregs " появляется в начале функции, в противоположность тому, где обращение к " __ builtin_saveregs " написано, независимо от того, что это за код. Поэтому регистры должны быть сохранены перед тем, как функция начинает их использовать для собственных целей. Используйте эту встроенную функцию, чтобы найти первые безымянные параметры в регистрах. Вообще, машина может иметь несколько категорий регистров, используемых для параметров, каждый для специфической категории типов данных. (Например, на некоторых машинах регистры с плавающей запятой используются для параметров с плавающей запятой, в то время как другие параметры передаются в общих регистрах.) Чтобы добиться того, чтобы не-varargs функции использовали соответствующее соглашение о вызовах, Вы должны определить Тип данных " CUMULATIVE_ARGS " для записи того, сколько регистров в каждой категории использовано. " __ builtin_args_info " обращается к той же самой структуре данных типа " CUMULATIVE_ARGS " после того, как обычное размещение параметра законченный с ним, с CATEGORY, определяющей, к какому слову обращаться. Таким образом, значение указывает первый неиспользуемый регистр в данной категории. Обычно Вы использовали бы " __ builtin_args_info " в реализации " va_start " для доступа к каждой категории только однажды и сохранения значения в объекте ` va_list '. Поэтому " va_list " должна будет обновлять значения, и нет никакого способа изменить значения, обращаясь при помощи " __ builtin_args_info ". Это - эквивалент " __ builtin_args_info ", для стековых параметров. Он возвращает адрес первого безымянного стекового параметр, как тип " void * ". Если " ARGS_GROW_DOWNWARD ", он возвращает адрес ячейки выше первого безымянного стекового параметра. Используйте его в " va_start ", чтобы инициализировать указатель для выборки параметров из стека. Также используйте его в " va_start " , чтобы проверить, что второй параметр LASTARG - последний именованный параметр текущей функции. Так как каждая машина имеет собственные соглашения, какие типы данных могут передаваться в каких видах регистров, ваша реализация " va_arg " должна воплотить эти соглашения. Самый простой способ категоризировать определенный тип данных заключается в использовании " __ builtin_classify_type " вместе с " sizeof " и " __ alignof __ ". " __ builtin_classify_type " игнорирует значение OBJECT, рассматривая только тип данных. Он возвращает целое число, описывающее, какой это тип - целое число, плавающее, указатель, структура и так далее. Файл " typeclass.h " определяет перечисление, к которому Вы можете использовать для интерпретации значений " __ builtin_classify_type ". Эта макрокоманды машинного описания помогают выполнять varargs: Если определено, является выражением C, которое генерирует машинно-специфический код для обращения к " __ builtin_saveregs ". Этот код будет помещен в самое начало функции, перед тем, как будет получен доступ к какому-либо параметру. Возвращаемое значение этой функции должно быть RTX, которое содержит значение, которое следует использовать как результат " __ builtin_saveregs ". Параметр ARGS - " tree_list ", содержащий параметры, которые были переданы " __ builtin_saveregs ". Если эта макрокоманда не определена, транслятор выведет обычный вызов библиотечной функции " __ builtin_saveregs ". Эта макрокоманда предлагает альтернативный вариант использования " __ builtin_saveregs " и определения макрокоманды " EXPAND_BUILTIN_SAVEREGS ". Используйте его, чтобы сохранить безымянные регистровые параметры в стеке так, как будто все параметры были переданы последовательно в стек. Как только это сделано, Вы можете использовать стандартную реализацию varargs, которая работает на машинах, которые передают все параметры через стек. Параметр ARGS_SO_FAR - структура данных " CUMULATIVE_ARGS ", содержащая значения, которые получаются после обработки именованных параметров. Параметры MODE и TYPE описывают последний именованный параметр - его машинный тип и тип данных как узел дерева. Реализация макрокоманды должна делать две вещи: первое, поместить в в стек все регистровые параметры, *не* используемые для именованных параметров, и второе, сохранить размер этих записанных в стек данных, в переменной типа " int ", чье имя поддерживается как параметр PRETEND_ARGS_SIZE. Значение, которое Вы сохраняете, будет служить как дополнительное смещение для установки кадра стека. Поскольку Вы должны генерировать код, чтобы поместить в стек безымянные параметры во время компиляции, не зная их типов данных, " SETUP_INCOMING_VARARGS " имеет смысл использовать только на машинах, которые имеют только одну категорию регистровых параметров и используют ее одинаково для всех типов данных. Если параметр SECOND_TIME отличен от нуля, это означает, что параметры функции анализируются второй раз. Так случается для встроенной функции, которая фактически не компилируется до конца исходного файла. Макрокоманда " SETUP_INCOMING_VARARGS " не должна генерировать никаких команд для этого случая. Определите эту макрокоманду, если место, где передается параметр функции, зависит от того, является ли он именованным параметром. Эта макрокоманда управляет тем, как устанавливается параметр NAMED к " FUNCTION_ARG " для функций varargs и stdarg. Если эта макрокоманда определена, параметр NAMED - всегда истина для именованных параметров и ложь - для безымянных параметров. Если она не определена, но " SETUP_INCOMING_VARARGS " определена, то все параметры обрабатываются как именованные. В противном случае, все именованные параметры, за исключением последнего, обрабатываются как именованные. 6.9. Trampolines для вложенных функций "Trampoline" - маленькая часть кода, которая создается во время выполнения, когда принимается адрес вложенной функции. Она обычно располагается в стеке, во кадре стека содержащей функции. Эти макрокоманды сообщают GNU CC, как генерировать код, чтобы распределять и инициализировать trampoline. Команды в trampoline должны делать две вещи: загрузка константного адреса в статический цепной регистр, и переход к реальному адресу вложенной функции. На CISC машинах типа m68k это требует двух команд, перемещение и переход. Тогда два адреса присутствуют в trampoline как непосредственные операнды размера word. На RISC машинах часто необходимо загрузить каждый адрес в регистр в двух частях. Тогда части каждого адреса образуют отдельные непосредственные операнды. Код, сгенерированный, чтобы инициализировать trampoline, должен сохранить переменные части - статическое цепное значение и адрес функции - в непосредственные операнды команд. На CISC машине это просто вопрос копирования каждого адреса к ссылку памяти в соответствующее смещение от начала trampoline. На RISC машине может оказаться необходимо выбирать части адреса и сохранять их отдельно. Оператор C для вывода в поток FILE кода ассемблера для блока данных, который содержит постоянные части trampoline. Этот код не должен включать метку - о метке позаботятся автоматически. Имя подпрограммы для включения в раздел, в который должен быть помещен trampoline шаблон (* См.: Разделы::.). Значение по умолчанию - значение " readonly_data_section ", который помещает trampoline в разделе, содержащем данные только для чтения. Выражение C для размера в байтах trampoline, как целое число. Выравнивание, требуемое для trampolines, в битах. Если Вы не определяете эту макрокоманду, для выравнивания trampolines используется значение " BIGGEST_ALIGNMENT ". Оператор C для инициализации переменных частей trampoline. ADDR - RTX для адреса trampoline; FNADDR - RTX для адреса вложенной функции; STATIC_CHAIN - RTX для статического цепного значения, которое должно быть передано функции, когда она вызывается. Выражение C для распределения места во время выполнения для trampoline. Значение выражения должно быть RTX, представляющим ссылку памяти на место для trampoline. Если эта макрокоманда не определена, по умолчанию trampoline распределяется как слот стека. Это значение по умолчанию подходит для большинства машин. Исключения - машины, где невозможно выполнение команд в области стека. На таких машинах Вам, возможно, следует создать отдельный стек, используя эту макрокоманду в сочетании с " FUNCTION_PROLOGUE " и " FUNCTION_EPILOGUE ". FP указывает на структуру данных, " struct function", которая описывает состояние трансляции непосредственных содержащий функции для функции, которой принадлежит trampoline. Обычно (когда " ALLOCATE_TRAMPOLINE " не определено), слот стека для trampoline находится во кадре стека этой содержащей функции. Другие cтратегии распределения, вероятно, должны делать что-либо аналогичное с этой информацией. Выполнение trampolines трудно на многих машинах, потому что они имеют раздельные кэши команд и данных. Запись в ячейку стека не может очистить память в кэше команд, так что когда программа переходит к этой ячейке, выполняется старое содержимое. Имеется два возможных решения. Одно заключается в очистить относящихся к делу частей кэша команд всякий раз, когда trampoline устанавливается. Другой - сделать все trampoline идентичными, сделав в них переходы на стандартную подпрограмму. Вышеупомянутая методика делает выполнение trampoline быстрее; последний способ делает инициализацию быстрее. Чтобы очистить кэш команд, когда trampoline инициализирован, определите следующие макрокоманды, которые описывают форму кэша. Полный размер в байтах кэша. Длина в байтах каждой линии кэша. Кэш разделен на линии кэша, которые являются непересекающимися слотами, каждый из которых содежит часть данных, выбранных из памяти. Каждый раз, когда данные записываются в кэш, вся линия читается целиком. Данные, загруженные в линию кэша, всегда выравниваются на границе, равной размеру линии. Число альтернативных линий кэша, которые могут содержать любую конкретную ячейку памяти. Альтернативно, если машина имеет системные вызовы или команды для очистки кэша команды непосредственно, Вы можете определять следующую макрокомандуа. Если определена, расширяется до выражения C, очищающего *кэш команд* в указанном интервале. Если она не определена, и макрокоманда INSN_CACHE_SIZE определена, генерируется некоторый родовой код для очистки кэша. Определение этой макрокоманды обычно является цепочкой операторов " asm ". BEG и END - выражения-указатели. Чтобы использовать стандартную подпрограмму, определите следующую макрокоманду. Кроме того, Вы должны удостовериться, что команды в trampoline заполняют всю линию кэша идентичными командами, или же гарантируют, что начало trampoline кода всегда выравнивается по той же самой точке на линии кэша. Смотрите " m68k.h " как руководство. Определите эту макрокоманду, если trampolines нуждаются в специальной подпрограмме для выполнения их работы. Макрокоманда должна расширяться до ряда " asm " операторов, которые будут компилироваться GNU CC. Они входят в библиотечную функцию под названием " __ transfer_from_trampoline ". Если Вам нужно избежать выполнения кода обычного пролога cкомпилированной функции C, когда Вы переходите к подпрограмме, Вы можете добиться этого, помещая Вашу собственную специальную метку в коде ассемблера. Использование один оператор " asm ", чтобы сгенерировать метку ассемблера, и другой, чтобы сделать метку глобальной. Тогда trampolines смогут использовать эту метку для перехода непосредственно к вашему специальному коду ассемблера. 6.10. Неявные Обращения к Библиотечным Подпрограммам Строковая константа C имени функции умножения одного знакового полного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию, "__mulsi3" функции, определенной в "libgcc.a". Строковая константа C имени функции деления одного знакового полного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию, "__divsi3" функции, определенной в "libgcc.a". Строковая константа C имени функции деления одного беззнакового полного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию " __udivsi3 " функции, определенной в "libgcc.a". Строковая константа C имени функции вычисления остатка деления одного знакового полного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию " __modsi3 " функции, определенной в "libgcc.a". Строковая константа C имени функции вычисления остатка деления одного беззнакового полного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию "__umodsi3" функции, определенной в "libgcc.a" Строковая константа C имени функции умножения одного знакового двойного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию "__muldi3" функции, определенной в "libgcc.a". Строковая константа C имени функции деления одного знакового двойного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию "__divdi3" функции, определенной в "libgcc.a". Строковая константа C имени функции деления одного беззнакового полного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию "__ divdi3" функции, определенной в "libgcc.a". Строковая константа C имени функции вычисления остатка деления одного знакового двойного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию "__moddi3" функции, определенной в "libgcc.a". Строковая константа C имени функции вычисления остатка деления одного беззнакового полного слова на другое. Если вы не определяете эту макрокоманду, используется имя, заданное по умолчанию "__umoddi3" функции, определенной в "libgcc.a". Определите эту макрокоманду как оператор C, который объявляет дополнительные библиотечные подпрограммы и переименовывает существующие. "Init_optabs" вызывает эту макрокоманду после инициализации всех нормальных библиотечных подпрограмм. Значение "EDOM" на целевой машине в виде целочисленной константы C. Если вы не определяете эту макрокоманду, GNU CC не пытается вносить значение "EDOM" в "errno" непосредственно. Посмотрите в "/usr/include/errno.h" чтобы найти значение "EDOM" для вашей системы. Если вы не определяете "TARGET_EDOM", то откомпилированный код сообщит об ошибке, заставляя это делать библиотечную функцию. Если математические функции на вашей системе при ошибке используют "matherr", то вы должны оставить "TARGET_EDOM" неопределенным, чтобы "matherr" могла нормально использоваться. Определите эту макрокоманду как выражение C для создания выражения rtl, которое ссылается на глобальную "переменную" "errno". (На некоторых системах, "errno" может не быть переменной.) Если вы не определяете эту макрокоманду, используется приемлемое значение по умолчанию. Определите эту макрокоманду, если GNU CC должен генерировать обращения к библиотечным функциям System V (и ANSI C) "memcpy" и "memset", а не к BSD-функциям "bcopy" и "bzero". Определите эту макрокоманду только если параметры типа "float" не могут быть переданы библиотечным подпрограммам (так что они должны быть преобразованы в "double"). Эта макрокоманда воздействует и на то, как генерируются библиотечные вызовы и как библиотечные подпрограммы в "libgcc1.c" принимают свои параметры. Это полезно на машинах, где параметры с плавающей и фиксированной точкой передаются по-разному, например в i860. Определите эту макрокоманду, чтобы изменить тип, используемый библиотечными подпрограммами, чтобы передавать параметры типа "float". (По умолчанию, они используют объединение "float" и "int".) Очевидно выбор должен быть "float", но это не будет работать на традиционных трансляторах C, которые все параметры, объявленные как "float" переделывают в "double". Чтобы избежать такого преобразования, библиотечные подпрограммы запрашивают значение какого-то другого типа, а затем обрабатывают его как "float". На некоторых системах никакой другой тип не будет так работать. На этих системах вы должны вместо этой использовать "LIBGCC_NEEDS_DOUBLE", заставляя транслятор преобразовывать "double"-значения до того, как они переданы. Определите эту макрокоманду, чтобы изменить способ, которым библиотечные подпрограммы повторно назначают параметр типа "float" как "float" вместо типа, в качестве которого он был передан. По умолчанию это выражение, которое берет "float"-поле из объединения. Определите эту макрокоманду, чтобы изменить тип, используемый для возвращения значения библиотечными подпрограммами, которые должны возвращать тип "float". (По умолчанию они используют "int".) Очевидный выбор был бы "float", но это не будет работать на трансляторе традиционного C, который сам преобразовывает значения, объявленные как "float", в "double". Определите эту макрокоманду, чтобы изменить способ, каким образом значение, возвращаемое библиотечной подпрограммой, которая должна возвращать тип "float", должно быть упаковано, чтобы возвратить его. Эти функции объявляются как возвращающие тип "FLOAT_VALUE_TYPE" (обычно "int"). Эти значения не могут быть возвращены как тип "float" потому что традиционные C трансляторы сами преобразовывают значение в "double". Локальная переменная, названная "intify" всегда доступна, когда используется макрокоманда "INTIFY". Она - объединение "float"-поля с именем "f" и поля с именем "i" типа "FLOAT_VALUE_TYPE" или "int". Если вы не определяете эту макрокоманду, работает заданное по умолчанию определение : копирует значения через это объединение. Определите эту макрокоманду как имя типа данных, соответствующего "SImode" в собственном C-трансляторе системы. Вы не должны определять эту макрокоманду, если вышеуказанный тип - "long int", а это обычно он и есть. Определяйте эту макрокоманду как имя типа данных, соответствующего word_mode в собственном C-трансляторе системы. Вы не должны определять эту макрокоманду, если вышеуказанный тип - "long int", а это обычно так. Определите эти макрокоманды, чтобы обеспечить явные операторы C, выполняющие различные арифметические операции от типов "float" и "double" в библиотечных подпрограммах "libgcc1.c". См. в этом файле полный список этих макрокоманд и их параметров. На большинстве машин вы не должны определять эти макрокоманды, потому что C-транслятор, который приходит с системой, сам заботится об их создании. Определите эту макрокоманду, чтобы сгенерировать код для посылки сообщений в Objective C , используя соглашения о вызовах системы NeXT. Это соглашение о вызовах включает передачу объекта, селектора и аргументов метода всех сразу к поисковой функции библиотеки методов. Заданное по умолчанию соглашение о вызовах передает только объект и селектор к функции поиска, которая возвращает указатель на метод. 6.11. Способы адресации Здесь содержится информация о способах адресации. Определите эту макрокоманду, если машина поддерживает постинкрементную адресацию. Аналогично и для других способов адресации. Выражение C, которое равно 1, если RTX X - константа, содержащая правильный адрес. На большинстве машин, оно может быть определено как "CONSTANT_P(X)", но на некоторых машинах больше ограничений на поддерживаемые базовые адреса. неизвестны, такие как "symbol_ref", "label_ref", "high"-выражения и "const"-арифметические выражения в дополнение к выражениям "const_int" и "const_double". Максимальное число регистров, которые могут появляться в правильном адресе памяти. Обратите внимание, что это вы должны определять значение, равное максимальному числу, которое может принимать "GO_IF_LEGITIMATE_ADDRESS". Сложный оператор C с условным оператором "goto LABEL;", выполняющимся, если X (RTX) - правильный адрес в памяти на целевой машине для операнда памяти типа MODE. Обычно лучше определить несколько более простых макрокоманд, служащих подпрограммами для этой. В противном случае эта макрокоманда может оказаться слишком сложной для понимания. Эта макрокоманда должна существовать в двух вариантах: строгом и нестрогом. Строгий вариант используется в процессе перезагрузки. Она должна быть определена так, чтобы любой псевдорегистр, который не был распределен в аппаратном регистре, считался ячейкой памяти. В контекстах, где требуется некоторый регистр, следует отклонить псевдорегистр, не представленный аппаратным. Нестрогий вариант используется в других проходах. Она должна быть определена, чтобы принимать все псевдорегистры в каждом контексте, где требуется некоторый регистр. Исходные файлы транслятора, которые используют строгий вариант этой макрокоманды, определяют макрокоманду "REG_OK_STRICT". Вы должны использовать условную директиву "#ifdef REG_OK_STRICT", чтобы определить строгий вариант в этом случае, иначе - нестрогий вариант. Подпрограммы для проверки приемлемых регистров для различных целей (одна для базовых регистров, одна для индексных регистров, и так далее) - типичны среди подпрограмм, используемых для определения "GO_IF_LEGITIMATE_ADDRESS". Только эти подпрограммы-макрокоманды должны иметь два варианта; макрокоманды более высоких уровней тоже могут быть строгими или нет. Обычно, постоянные адреса, которые являются суммой "symbol_ref" и целого числа, располагаются внутри "const" RTX, чтобы отметить их как константы. Следовательно, не надо распознавать такие суммы как правильные адреса. Обычно вы просто распознаете любую "const" как правильную. Обычно "PRINT_OPERAND_ADDRESS" не подготовлена к обработке константных сумм, которые не отмечены "const". Она принимает, что явный "plus" указывает на индексацию. Если так, то вы должны отбросить такие явные постоянные суммы как неправильные адреса, так чтобы ни одна из них не была передана "PRINT_OPERAND_ADDRESS". Является ли символический адрес правильным, зависит от раздела, к которому адрес обращается (на некоторых машинах). На таких машинах определите макрокоманду "ENCODE_SECTION_INFO", чтобы сохранить информацию в "symbol_ref", и затем проверите ее там. Когда вы видите "const", вы должны будете заглянуть внутрь нее, чтобы найти "symbol_ref" для определения раздела. *Обратите внимание на Ассемблерный Формат::. Самый лучший способ изменить строку имени - добавить текст к началу с подходящей пунктуацией, чтобы предотвратить любую неоднозначность. Распределите новое имя в "saveable_obstack". Вы должны будете изменить "ASM_OUTPUT_LABELREF", чтобы удалить и декодировать добавленный текст и вывести имя соответственно, и определить "STRIP_NAME_ENCODING", чтобы обратиться к первоначальной строке имени. Вы можете проверить информацию, сохраненную в "symbol_ref" в определениях макрокоманд "GO_IF_LEGITIMATE_ADDRESS" и "PRINT_OPERAND_ADDRESS". Выражение C, которое отлично от нуля если X (считающийся "reg" RTX) подходит для использования в качестве базисного регистра. Для аппаратных регистров оно должно принимать только разрешенные аппаратными возможностями и отклонять остальные. Отклоняет ли макрокоманда псевдорегистры, должен контролироваться "REG_OK_STRICT", как описано выше. Это обычно требует двух различных вариантов определения, причем "REG_OK_STRICT" управляет, какое фактически используется. Выражение C, которое отлично от нуля если X (считающийся "reg" RTX) годится для использования как индексный регистр. Различие между индексным регистром и основным регистром в том, что индексный регистр может масштабироваться. Если адрес включает сумму двух регистров, ни один из которых не масштабирован, то любой может быть помечен как "основной", а другой как "индексный"; но какая бы пометка ни использовалась, она должна удовлетворить ограничениям машины, регистры которой могут служить только в определенных местах. Транслятор пробует оба способа, ища правильный, и перезагрузит один или оба регистров только, если никакой способ не сработает. Сложный оператор C, который пытается заменять X на правильный адрес в памяти для операнда типа MODE. WIN будет меткой оператора в C в другом месте в коде; определение макрокоманды может использовать : GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN); во избежание дальнейшей обработки, если адрес стал правильным. X будет всегда результатом обращения к "break_out_memory_refs", а OLDX будет операндом, который был передан той функции, чтобы получить X. Код, сгенерированный этой макрокомандой не должен изменить структуру X. Если он трансформирует X в более правильную форму, он должен назначить X (который будет всегда C переменная) новое значение. Эта макрокоманда необязательно выдаст правильный адрес. Транслятор имеет стандартные пути выполнения этого всех случаях. Фактически, этой макрокоманде безопасно ничего не делать. Но часто машинно-зависимая стратегия может привести к лучшему коду. Оператор C или составной оператор с условным "goto LABEL; ", выполняемым, если адрес в памяти X (RTX) может иметь различные значения, в зависимости от машинного типа ячейки памяти, для которой он используется или если адрес правильный для некоторых типов, но не для других. Автоинкрементные и автодекрементные адреса обычно имеют зависимые от типа эффекты, потому что размер приращения или декремента - размер адресуемого операнда. Некоторые машины имеют другие зависимые от типа адреса. Многие RISC-машины имеют независимые от типа адреса. Вы можете принять, что ADDR - правильный адрес для данной машины. Выражение C, которое отлично от нуля, если X - правильная константа для непосредственного операнда на целевой машине. Вы можете принять, что X удовлетворяет "CONSTANT_P", так что вам не надо проверять это. Фактически, "1" является подходящим определением для этой макрокоманды на машинах, где какая-нибудь "CONSTANT_P" правильна. 6.12. Состояние Кода Условия Описание состояния кода условия. Файл "conditions.h" определяет переменную "cc_status" для описания того, как был вычислен код условия (в случае, если интерпретация кода условия зависит от команды, которой он был установлен). Эта переменная содержит выражения RTL, на которых основан код условия, и несколько стандартных флажков. Иногда дополнительные машинно-специфические флаги должны быть определены в заголовочном файле описания машины. Он может также добавлять дополнительную машинно-специфическую информацию, определяя "CC_STATUS_MDEP". C-код для типа данных, который используется для объявления "mdep" компонент "cc_status". Его значение по умолчанию : "int". Эта макрокоманда не используется на машинах, которые не используют "cc0". Выражение C для инициализации поля "mdep" в "empty". Заданное по умолчанию определение не делает ничего, так как большинство машин не использует это поле. Если вы хотите использовать поле, вы должны, вероятно, определить эту макрокоманду, чтобы инициализировать его. Эта макрокоманда не используется на машинах, которые не используют "cc0". Составной оператор C для установки компонент "cc_status" соответственно для insn INSN, тело которого - EXP. Эта макрокоманда отвечает за распознавание insn, которые устанавливают код условия как побочный эффект другого действия, такого как установка "(cc0)". Эта макрокоманда не используется на машинах, которые не используют "cc0". Если имеются insn, которые не устанавливают код условия, но изменяют другие машинные регистры, эта макрокоманда должна проверить, портят ли они выражения, код условия которых зарегистрирован как отраженный. Например, на 68000, insn, которые сохраняют значения в регистрах адреса, не устанавливают код условия, что означает что обычно "NOTICE_UPDATE_CC" может оставлять "cc_status" неизменным для таких insn. Но предположите, что предыдущий insn установил код условия, основанный на расположении "a4@ (102)", а текущий insn сохраняет новое значение в "a4". Хотя код условия не изменен, он никогда не будет отражать содержимое "a4@ (102)". Следовательно, "NOTICE_UPDATE_CC" должен изменить "cc_status" в этом случае, чтобы сообщить, что ничто не известно относительно значения кода условия. Определение "NOTICE_UPDATE_CC" должно быть готово иметь дело с результатами peephole-оптимизации: insn, чьи образцы "параллельны" RTX, содержащим различные "reg", "mem" или константы, которые являются только операндами. RTL - структуры этих insn недостаточно, чтобы указать, что insn фактически делают. Что "NOTICE_UPDATE_CC" должен делать, когда видит, что каждый должен только выполнить "CC_STATUS_INIT". Возможное определение "NOTICE_UPDATE_CC" состоит в вызове функции, которая смотрит на атрибут (*Обратите внимание на Insn Атрибуты::.), например, "cc". Это избавляет от детальной информации относительно образцов в двух местах : в "md" файле и в "NOTICE_UPDATE_CC". Список имен, которые нужно использовать для дополнительных режимов для значений кода условия в регистрах (*Обратите внимание на Образцы Переходов::.). Эти имена добавлены к "enum machine_mode", и все они имеют класс "MODE_CC". В соответствии c соглашением, они должны начинаться с "CC" и заканчиваться на "mode". Вы должны определить эту макрокоманду, только, если ваша машина не использует "cc0" и только, если требуются дополнительные типы. Список C-строк имен для типов, перечисленных в "EXTRA_CC_MODES". Например, Sparc определяет эту макрокоманду и "EXTRA_CC_MODES" так : #define EXTRA_CC_MODES CC_NOOVmode, CCFPmode, CCFPEmode #define EXTRA_CC_NAMES "CC_NOOV", "CCFP", "CCFPE" Эта макрокоманда не требуется, если "EXTRA_CC_MODES" не определена. Возвращает тип из класса "MODE_CC" для использования, когда код операции сравнения OP применяется к rtx X и Y. Например, на Sparc, "SELECT_CC_MODE" определена, как описано в "Образцы Переходов::.". #define SELECT_CC_MODE(OP,X,Y) \ (GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \ ? ((OP == EQ || OP == NE) ? CCFPmode : CCFPEmode) \ : ((GET_CODE (X) == PLUS || GET_CODE (X) == MINUS \ || GET_CODE (X) == NEG) \ ? CC_NOOVmode : CCmode)) Вам не нужно определить эту макрокоманду, если "EXTRA_CC_MODES" не определена. На некоторых машинах не все возможные сравнения определены, но вы можете получить недопустимое сравнение через допустимые. Например, на Alpha нет "GT"-сравнения, но вы можете использовать вместо него "LT"-сравнение и поменять порядок операндов. На таких машинах определите эту макрокоманду, как оператор C, делающий любые требуемые преобразования. CODE - начальный код сравнения, OP0 и OP1 - левые и правые операнды сравнения, соответственно. Вы можете изменить CODE, OP0, и OP1, если требуется. GNU CC не будет считать сравнение, полученное в этой макрокоманде, правильным, но посчитает, если возникающий в результате insn соответствует образцу в "md" файле. Вам не нужно определять эту макрокоманду, если она никогда не изменяла бы код сравнения или операнды. Выражение C, значение которого - 1, если всегда можно безопасно обратить сравнение типа MODE. Если "SELECT_CC_MODE" может когда-либо возвращать MODE для неравенства с плавающей точкой, то "REVERSIBLE_CC_MODE (MODE)" должно быть 0. Вам не нужно определять эту макрокоманду, если она всегда возвращала бы ноль или если формат с плавающей точкой отличается от "IEEE_FLOAT_FORMAT". Например, вот определение, используемое на Sparc, где неравенства с плавающей точкой всегда даны "CCFPEmode": #define REVERSIBLE_CC_MODE(MODE) ((MODE) != CCFPEmode) 6.13. Описание Относительных Стоимостей Операций Эти макрокоманды позволяют Вам описывать относительное быстродействие различных операций на целевой машине. Часть C "switch"-оператора, которая описывает относительные издержки постоянных выражений RTL. Она должна содержать "case"-метки для кодов выражений "const_int", "const", "symbol_ref", "label_ref" и "const_double". Каждый "case" должен в конечном счете достигнуть оператора "return", чтобы возвратить относительную стоимость использования данного вида константы в выражении. Стоимость может зависеть от точного значения константы, которая доступна для исследования в X, и rtx-кода выражения, в котором содержится, и который может быть найден в OUTER_CODE. CODE - код выражения - избыточный, так как может быть получен, как "GET_CODE(X)". То же, что "CONST_COSTS", но применяется к неконстантным выражениям RTL. Может использоваться, например, для указания, насколько дорогостоящей является команда умножения. В написании этой макрокоманды, вы можете использовать конструкцию "COSTS_N_INSNS (N)" чтобы определить стоимость, равную N, быстрых команд. OUTER_CODE - код выражения, в котором содержится X. Эту макрокоманду можно не определять, если заданные по умолчанию предположения стоимости адекватны для целевой машины. Выражение, дающее стоимость способа адресации, который содержит ADDRESS. Если не определена, то стоимость вычисляется из выражения ADDRESS и значения "CONST_COSTS". Для большинства CISC машин, заданная по умолчанию стоимость - хорошее приближение истинной стоимости способа адресации. Однако, на RISC машинах, все команды обычно имеют ту же самую длину и время выполнения. Следовательно все адреса будут иметь равные издержки. В случаях, где известна более, чем одна форма адреса, будет использоваться форма с самой низкой стоимостью. Если многие формы имеют одну и ту же самую низкую стоимость, то будет использоваться та, которая наиболее сложна. Например, представьте, что адрес, который является суммой регистра и константы, используется дважды в одном базисном блоке. Когда эта макрокоманда не определена, адрес будет вычислен в регистре, и ссылки на память будут косвенными, через этот регистр. На машинах, где стоимость способа адресации, содержащего сумму, не выше чем простой косвенной ссылки, она произведет дополнительную команду и, возможно, потребует дополнительного регистра. Правильная спецификация этой макрокоманды устраняет эту добавку для таких машин. Подобным образом эта макрокоманда используется при оптимизации циклов. ADDRESS должен быть правильным, как адрес. В таком случае, стоимость не имеет значения и может быть принята произвольной; недопустимым адресам не должны присваиваться различные стоимости. На машинах, где адрес, включающий больше, чем один регистр, - такой же дешевый, как адреса, включающий только один регистр, определение "ADDRESS_COST", для отражения этого, может заставить два регистра жить в области кода, где был бы только один, если "ADDRESS_COST" не была бы определена. Этот эффект должен рассматриваться в определении этой макрокоманды. Эквивалентные издержки должны быть приданы, вероятно, только адресам с различными числами регистров на машинах с очень большим количеством регистров. Эту макрокоманду можно не определять или определять, как константу. Выражение C стоимости перемещения данных из регистра класса FROM в регистр класса TO. Классы выражены используя порядковые значения "GENERAL_REGS". Значение 4 - значение по умолчанию; другие значения интерпретируются относительно этого. Не требуется, чтобы стоимость всегда равнялась 2, если FROM = TO; на некоторых машинах дорого перемещать данные между регистрами, если они - не регистры общего назначения. Если перезагрузка видит insn, состоящий из одиночного "set" между двумя аппаратными регистрами, и если "REGISTER_MOVE_COST" для их классов возвращает значение 2, перезагрузка не проверяет, что ограничения insn выполнены. Установка стоимости на значение, не равное 2, позволяет перезагрузке проверять, что ограничения выполнены. Вы должны делать это, если "movM" ограничения на образец не позволяют такое копирование. Выражение C стоимости перемещения данных типа M между регистром и памятью. По умолчанию : 2; это стоимость относительно стоимости в "REGISTER_MOVE_COST". Если копирование между регистрами и памятью дороже, чем между двумя регистрами, вы должны определить эту макрокоманду, чтобы выразить относительную стоимость. Выражение C стоимости команды перехода (ветвления). По умолчанию : 1; другие значения интерпретируются относительно этого. Вот дополнительные макрокоманды, которые определяют не точные относительные издержки, а некоторые действия, которые более дороги, чем GNU CC обычно ожидает. Определите эту макрокоманду как выражение C, которое отлично от нуля, если обращение менее, чем в слову в памяти (то есть к "char" или "short") не быстрее, чем обращение к слову, то есть, если такой доступ требует более одной команды или если нет различия в стоимости загрузки байта и (выровненного) слова. Когда эта макрокоманда не определена, транслятор обратится к полю, находя самый маленький содержащийся объект; когда определена, будет использоваться загрузка полного слова, если разрешает выравнивание. Если к доступ к байтам не быстрее, чем доступ к словам, использование слов предпочтительно, так как может устранить последующее обращение к памяти, если последующие обращения происходят к другим полям в том же слове из структуры, но к различным байтам. Определите эту макрокоманду, если расширение нулем (типов "char" или "short" до "int") может быть выполнено быстрее, если адресат - регистр, про который известно, что он - ноль. Если вы определяете эту макрокоманду, вы должны иметь образцы команды, которые распознают структуры RTL подобно этому: (set (strict_low_part (subreg:QI (reg:SI ...) 0)) ...) и аналогично для "HImode". Определите эту макрокоманду, равной 1, если доступ к невыровненным данным имеет стоимость во много раз большую, чем доступ к выровненным данным, например, если они эмулированы в обработчике ловушки. Когда эта макрокоманда отлична от нуля, транслятор будет действовать, как будто "STRICT_ALIGNMENT" была отлична от нуля при генерации кода для блочного копирования. Это может заставлять транслятор генерировать гораздо больше инструкций. Следовательно, не устанавливайте эту макрокоманду в отличное от нуля значение, если доступ к невыровненным данным только добавляет цикл или два к времени доступа к памяти. Если значение этой макрокоманды - всегда ноль, ее не нужно определять. Определите эту макрокоманду, чтобы запретить уменьшение количества адресов памяти. (На некоторых машинах, такое уменьшение количества делает больше вреда, чем пользы.) Количество insn, занимающихся скалярным перемещением, которые должны быть сгенерированы вместо insn, реализующего строковое перемещение или вызова библиотечной функции. Увеличение значения будет всегда делать код более быстрым, но в конечном счете сильно увеличивает размер кода. Если вы не определяете эту макрокоманду, используется приемлемое значение по умолчанию. Определите эту макрокоманду, если вызвать постоянную функцию не дороже, чем по адресу в регистре. Определите эту макрокоманду, если функции не дороже вызвать саму себя по явному адресу, чем по адресу в регистре. Оператор C (без точки с запятой) для модификации целой переменной COST (стоимость) , основанную на связи между INSN, который зависит от DEP_INSN через LINK зависимость. По умолчанию не производится корректировка COST. Это может использоваться например, чтобы указать планировщику, что вывод или анти-зависимость не несет ту же самую стоимость как зависимость данных. 6.14. Деление Вывода на Разделы Объектный файл разделен на разделы, содержащие различные типы данных. В наиболее общем случае, имеются три раздела: "текстовый раздел", который содержит команды и данные только для чтения; "раздел данных", который содержит инициализированные перезаписываемые данные; и "раздел bss", который содержит неинициализированные данные. Некоторые системы имеют другие виды разделов. Транслятор должен сообщить ассемблеру, когда переключать разделы. Эти макрокоманды управляют тем, какие команды вывести, чтобы сообщить об этом ассемблеру. Вы можете также определять дополнительные разделы. Выражение C, значение которого - строка, содержащая ассемблерную операцию, которая должна предшествовать командам и данным только для чтения. Обычно ".text". Выражение C, значение которого - строка, содержащая ассемблерную операцию для идентификации следующих данных как перезаписываемые инициализированные данные. Обычно ".data". Если определена, то это выражение C, значение которого - строка, содержащая ассемблерную операцию для идентификации следующих данных как разделяемых данных. Если не определена, будет использоваться "DATA_SECTION_ASM_OP". Если определена, это выражение C, значение которого - строка, содержащая ассемблерную операцию для идентификации следующих данных как кода инициализации. Если не определена, GNU CC считает, что такого раздела не существует. Список имен для разделов, отличных от двух стандартных, которые являются "in_text" и "in_data". Вы не должны определять эту макрокоманду, если на системе нет других разделов (которые GCC должен использовать). Одна или больше функций, которые определены в "varasm.c". Эти функции должны производить действия, аналогичные функциям из "text_section" и "data_section", но для ваших дополнительных разделов. Не определяйте эту макрокоманду, если вы не определили "EXTRA_SECTIONS". На большинстве машин переменные только для чтения, константы и таблицы переходов помещены в текстовый раздел. Если на вашей машине это не так, эта макрокоманда должна быть определена именем функции (из "data_section" или определенной в "EXTRA_SECTIONS") которая переключается на соответствующий раздел, который используется для элементов только для чтения. Если эти элементы были помещены в текстовый раздел, эту макрокоманду не нужно определять. Оператор(ы) C для переключения на соответствующий раздел для вывода EXP. Вы можете считать, что EXP - или "VAR_DECL" узел, или константа некоторого типа. RELOC указывает, требует ли начальное значение EXP настроек времени компоновки. Выберите раздел, вызывая "text_section" или одну из альтернатив для других разделов. Не определяйте эту макрокоманду, если вы помещаете все переменные только для чтения и константы в раздел данных только для чтения (обычно текстовый раздел). Оператор(ы) C для переключения на соответствующий раздел для вывода RTX в типе MODE. Вы можете принять, что RTX - некоторая константа в RTL. Параметр MODE избыточен за исключением случая "const_int" rtx. Выберите раздел, вызывая "text_section" или одну из альтернатив для других разделов. Не определяйте эту макрокоманду, если вы помещаете все константы в раздел данных только для чтения. Определите эту макрокоманду, если таблицы перехода (для "tablejump" insns) должны быть выведены в текстовый раздел, наряду с командами ассемблера. Иначе используется раздел данных только для чтения. Эта макрокоманда не имеет значения, если нет отдельного раздела данных только для чтения. Определите эту макрокоманду, если ссылки на символ должны обрабатываться по-разному в зависимости от переменной или функции, названной символом (так же как, в каком разделе она находится). Определение макрокоманды, если есть, выполняется немедленно после того, как rtl для DECL был создан и сохраняется в "DECL_RTL (DECL)". Значение rtl будет "mem", адрес которого - "symbol_ref". Обычная вещь для этой макрокоманды : записывать флажок в "symbol_ref" (типа "SYMBOL_REF_FLAG") или сохранять измененную строку имени в "symbol_ref" (если одного бита недостаточно для информации). Декодируйте SYM_NAME, и сохраните реальную часть имени в VAR, без символов, которые кодируют информацию о разделе. Определите эту макрокоманду, если "ENCODE_SECTION_INFO" изменяет строку имени символа. 6.15. Позиционно-Независимый Код Этот раздел описывает макрокоманды, которые помогают генерировать позиционно-независимый код. Простое определение этих макрокоманд недостаточно для генерации правильного PIC; вы должны также добавить поддержку макрокоманд "GO_IF_LEGITIMATE_ADDRESS" и "PRINT_OPERAND_ADDRESS", также как "LEGITIMIZE_ADDRESS". Вы должны изменить определение "movsi" так, чтобы она выполняла соответствующее действие, когда исходный операнд содержит символический адрес. Вам может понадобиться также изменение обработки переключающих операторов так, чтобы они использовали относительные адреса. Номер регистра, используемого для адресации таблицы адресов статических данных в памяти. В некоторых случаях этот регистр определен в "application binary interface" (ABI). Когда эта макрокоманда определена, RTL сгенерирован для этого регистра один раз, как с указателем стека и регистрами указателя кадра. Если эта макрокоманда не определена, машинно-зависимые файлы обязаны распределить такой регистр (в случае необходимости). Определите эту макрокоманду, если регистр, определенный "PIC_OFFSET_TABLE_REGNUM", затирается (clobbered) обращениями. Не определяйте эту макрокоманду, если "PIC_OFFSET_TABLE_REGNUM" не определена. Генерируя позиционно-независимый код, когда две различных программы (A и B) совместно используют общую библиотеку (libC.a), текст библиотеки может быть разделен, в зависимости от того, скомпонована ли библиотека к тому же самому адресу к обеим программам. В некоторых из этих сред позиционно-независимый код требует не только использования различных способов адресации, но также специального кода разрешения использования этих способов адресации. Макрокоманда "FINALIZE_PIC" служит как ловушка, чтобы выдавать эти специальные коды, как только функция компилируется в ассемблерный код, но не раньше. (Это не выполнено раньше, потому что в случае компиляции inline-функции, это вело бы к большому числу прологов PIC, включаемых в функции, которые использовали inline-функции и компилировались в ассемблер.) Выражение C, которое отлично от нуля если X - правильный непосредственный операнд на целевой машине при генерации позиционно-независимого кода. Вы можете принять, что X удовлетворяет "CONSTANT_P", так что вы не должны проверять этого. Вы можете также принять, что FLAG_PIC истинен, так что вы не должны проверять этого. Вы не должны определять эту макрокоманду, если все константы (включая "SYMBOL_REF") могут быть непосредственными операндами при генерации позиционно-независимого кода. 6.16. Определение Выходного Языка Ассемблера Этот раздел описывает макрокоманды, принципиальная цель которых состоит в описании того, как писать команды на языке ассемблеров, а не того, что эти команды делают. 6.16.1. Полная Структура Ассемблерного Файла Описание полной структуры ассемблерного файла. Выражение C, которое выводит на stdio STREAM текст, которым должен начинаться ассемблерный файл. Обычно эта макрокоманда определяется, чтобы вывести строку, содержащую "#NO_APP", которое является комментарием, который не имеет эффекта на большинстве ассемблеров, но сообщает GNU ассемблеру, что он может не тратить время, проверяя некоторые конструкций ассемблера. На системах, которые используют SDB, необходимо вывести некоторые команды; см. "attasm.h". Выражение C, которое выводит на stdio STREAM текст, которым должен заканчиваться ассемблерный файл. Если эта макрокоманда не определена, по умолчанию ничего особенного не должно вывестись в конце файла. Большинство систем не требует никакого определения. На системах, которые используют SDB, необходимо вывести некоторые команды; см. "attasm.h". Оператор C вывода команд ассемблера, которые идентифицируют объектный файл как компилированный GNU CC (или другим GNU-транслятором). Если вы не определяете эту макрокоманду, выводится строка "gcc_compiled.:". Эта строка вычисляется, чтобы определить символ, который на BSD системах, никогда не будет определен по любой другой причине. GDB проверяет присутствие этого символа при чтении таблицы символов выполнимой программы. На не-BSD системах вы должны установить связь с GDB другим способом. Если GDB не используется на вашей системе, вы можете определить эту макрокоманду с пустым телом. Строковая константа C, описывающая, как начинать комментарий на целевом ассемблере. Транслятор принимает, что комментарий заканчивается в конце строки. Строковая константа C для текста, который нужно вывести перед каждым оператором (или последовательной группой) "asm". Обычно это - "#APP", который является комментарием, не имеющим эффекта на большинстве ассемблеров, но сообщающим GNU ассемблеру, что он должно проверить последующие строки на правильность ассемблерных конструкций. Строковая константа C для текста, который нужно вывести после каждого оператора (или последовательной группы) "asm". Обычно это - "#NO_APP", который говорит GNU ассемблеру экономить время, делая предположения, которые правильны для обычного вывода транслятора. Оператор C вывода COFF информации или DWARF-отладочной информации, который пишет на stdio поток STREAM, что файл с именем NAME - текущий исходный файл. Эта макрокоманду не надо определять, если подходит стандартная форма вывода для формата используемого файла. Оператор C вывода на stdio STREAM DBX или SDB отладочной информации перед кодом для строки номер LINE текущего исходного файла. Эта макрокоманду не надо определять, если стандартная форма отладочной информации для используемого отладчика, подходящая. Оператор C вывода чего-то в ассемблерный файл, чтобы обработать "#ident" директиву, содержащую текст STRING. Если эта макрокоманда не определена, ничего не выводится для "#ident" директивы. Оператор C вывода чего-то в ассемблерный файл, чтобы переключиться на раздел NAME для объекта DECL, который является "FUNCTION_DECL", "VAR_DECL" или "NULL_TREE". Некоторые целевые форматы не поддерживают произвольные разделы. Не определяйте эту макрокоманду в таких случаях. В настоящее время эта макрокоманда используется только чтобы поддерживать атрибуты раздела. Когда эта макрокоманда не определена, атрибуты раздела запрещены. Оператор C вывода любых ассемблерных операторов, которые должны предшествовать любым определениям Objective C объектов или посылке сообщений. Оператор выполняется только при компиляции программы на Objective C. 6.16.2. Вывод Данных Описание вывода данных. Операторы C вывода на stdio STREAM ассемблерной команды для трансляции константы с плавающей точкой из "TFmode", "DFmode", "SFmode", "TQFmode", "HFmode", или "QFmode", соответственно, значение которой - VALUE. VALUE - выражение C типа "REAL_VALUE_TYPE". Макрокоманды типа "REAL_VALUE_TO_TARGET_DOUBLE" полезны для написания этих определений. Оператор C вывода на stdio STREAM ассемблерной команды для трансляции целого числа длиной 16, 8, 4, 2 или 1 байт, соответственно, значение которого - VALUE. Параметр EXP будет выражением RTL, которое представляет постоянное значение. Используйте "output_addr_const(STREAM,EXP)" чтобы вывести это значение как ассемблерное выражение. Вам не нужно определять эту макрокоманду для размеров больших, чем "UNITS_PER_WORD", если действие макрокоманды было бы многократным вызовом макрокоманды, соответствующей размеру "UNITS_PER_WORD", по одному для каждого слова. Оператор C вывода на STREAM ассемблерной команды для трансляции одиночного байта, содержащего значение VALUE. Строковая константа C, дающая псевдооперацию, используемую для последовательности констант в один байт. Если эта макрокоманда не определена, по умолчанию - "byte". Оператор C вывода на stdio STREAM ассемблерной команды для трансляции строковой константы, содержащей LEN байт в PTR. PTR - выражение C типа "char*", а LEN - выражение C типа "int". Если ассемблер имеет ".ascii" псевдооперацию, как в Berkeley Unix ассемблере, не определяйте макрокоманду "ASM_OUTPUT_ASCII". Оператор C вывода ассемблерных команд для определения начала постоянного pool для функции. FUNNAME - строка, дающая имя функции. Если требуется тип возврата функции, он может быть получен через FUNDECL. SIZE - размер в байтах константного pool, который будет написан немедленно после этого обращения. Если pool-префикс не требуется (обычный случай), то не надо определять эту макрокоманду. Оператор C (с точкой с запятой или без нее) вывода константы в константный pool, если она требует специальной обработки. (Этой макрокоманде не требуется делать что бы то ни было для выражений RTL, которые могут выводиться обычным образом.) Параметр FILE - стандартный I/O-поток, для вывода ассемблерного кода. X - выражение RTL для константы, которую надо вывести, и MODE - тип (в случае, если X - "const_int"). ALIGN - требуемое выравнивание для значения X; вы должны вывести ассемблерную директиву, чтобы форсировать это выравнивание. Параметр LABELNO - номер, используемый во внутренней метке для адреса этого pool. Определение этой макрокоманды ответственно за вывод определения метки на соответствующее место. Вот как это делается : ASM_OUTPUT_INTERNAL_LABEL (FILE, Когда вы выводите ячейку pool особенным образом, вы должны завершить ее переходом "goto" к метке JUMPTO. Это предотвратит вторичный вывод той же ячейки обычным способом. Вам не нужно определять эту макрокоманду, если она ничего не делала бы. Определите эту макрокоманду как выражение C, которое отлично от нуля, если C используется как логический разделитель строк ассемблером. Если вы не определяете эту макрокоманду, то по умолчанию только символ ";" обрабатывается как логический разделитель строк. Эти макрокоманды определены как строковые константы C, описывающие синтаксис ассемблера для группировки арифметических выражений. Следующие определения правильны для большинства ассемблеров : #define ASM_OPEN_PAREN "(" #define ASM_CLOSE_PAREN ")" Эти макрокоманды обеспечиваются "real.h" для написания определений "ASM_OUTPUT_DOUBLE" и подобных : Они транслируют X типа "REAL_VALUE_TYPE" в целевое представление с плавающей точкой, и сохраняют битовый образец в массиве с элементами типа "long int", адрес которого - L. Число элементов в выводимом массиве определено размером желаемого целевого типа данных с плавающей точкой : 32 бита от него входят в каждый "long int" элемент массива. Каждый элемент массива содержит 32 бита результата, даже если "long int" на данной машине шире, чем 32 бита. Значения элементов массива разработаны так, чтобы вы могли печатать их, используя "fprintf", в порядке, в котором они должны появиться в памяти целевой машины. Эта макрокоманда преобразует X типа "REAL_VALUE_TYPE" к десятичному числу и сохраняет в виде строки STRING. Вы должны передать в STRING адрес достаточно длинного блока памяти, чтобы содержать результат. Параметр FORMAT - это "printf"-спецификация, которая служит предложением того, как форматировать строку вывода. 6.16.3. Вывод Неинициализированных Переменных Каждая из макрокоманд в этом разделе используется, чтобы выполнять всю работу по выводу одиночной неинициализированной переменной. Оператор C (без точки с запятой) для вывода в stdio поток STREAM ассемблерного определения общей метки NAME размером SIZE байт. Переменная ROUNDED - размер, округленный кверху до любого выравнивания которое требуется вызывающей функцией. Используйте выражение " assemble_name (STREAM, NAME) " для вывода самого имени; до и после которого, выводится дополнительный синтаксис ассемблера для определения имени и переход на новую строку. Эти макрокоманды контролируют, как выводятся ассемблерные определения неинициализированных глобальные переменные. Подобно " ASM_OUTPUT_COMMON ", за исключением того, что требует выравнивание как отдельный, явный параметр. Если Вы определяете эту макрокоманду, она используется вместо " ASM_OUTPUT_COMMON ", и дает Вам больше гибкости в обработке требуемого выравнивания переменной. Выравнивание определяется как число битов. Если определена, то подобна " ASM_OUTPUT_COMMON ", за исключением того, что она используется, если участвует NAME. Если она не определена, будет использоваться " ASM_OUTPUT_COMMON". Оператор C (без точки с запятой) для вывода в stdio поток STREAM определения ассемблера локальной общей метки NAME размером SIZE байтов. Переменная ROUNDED - это размер, округленный кверху до любого выравнивания, требуемого вызывающей функцией. Используйте выражение " assemble_name (STREAM, NAME) " для вывода самого имени; до и после него выводите дополнительный синтаксис ассемблера для определения имени и переход на новую строку. Эти макрокоманды контролируют, как выводятся ассемблерные определения неинициализированных статических переменных. Подобно " ASM_OUTPUT_LOCAL ", за исключением того, что требует выравнивание как отдельный, явный параметр. Если Вы определяете эту макрокоманду, она используется вместо " ASM_OUTPUT_LOCAL " и дает Вам большую гибкость в обработке требуемого выравнивания переменной. Выравнивание определяется как число битов. Если определено, подобно " ASM_OUTPUT_LOCAL ", за исключением того, что оно используется, если участвует NAME. Если не определено, будет использоваться " ASM_OUTPUT_LOCAL". 6.16.4. Вывод и Генерация Меток Это относится к выводу меток. Оператор C (без точки с запятой) для вывода в stdio поток STREAM ассемблерной метки NAME. Используйте выражение " assemble_name (STREAM, NAME) " для вывода имени непосредственно; до и после этого, выводится дополнительный синтаксис ассемблера для определения имени и переход на новую строку. Оператор C (без точки с запятой) для вывода в stdio поток STREAM любого текста, необходимого для объявления имени NAME определяемой функции. Эта макрокоманда отвечает за вывод определения метки (возможно использование "ASM_OUTPUT_LABEL "). Параметр DECL - это " FUNCTION_DECL " узел дерева, представляющего функцию. Если эта макрокоманда не определена, то имя функции определяется обычным способом как метка (посредством " ASM_OUTPUT_LABEL "). Оператор C (без точки с запятой) для вывода в stdio поток STREAM любого текста, необходимого для объявления размера функции, которая определяется. Параметр NAME - имя функции. Параметр DECL - это " FUNCTION_DECL " узел дерева, представляющего функцию. Если эта макрокоманда не определена, то размер функции не определен. Оператор C (без точки с запятой) для вывода в stdio поток STREAM любого текста, необходимого для объявления имени NAME инициализированной определяемой переменной. Эта макрокоманда должна выводить определение метки (возможно, используя " ASM_OUTPUT_LABEL "). Параметр DECL - " VAR_DECL " узел дерева, представляющего переменную. Если эта макрокоманда не определена, то имя переменной определяется обычным способом как метка (посредством " ASM_OUTPUT_LABEL "). Оператор C (без точки с запятой) для завершения объявления имени переменной, когда компилятор обработал инициализатор полностью и, таким образом, имел возможность определить размер массива, когда он контролировался инициализатором. Используется в системах, где необходимо объявлять что-либо относительно размера объекта. Если Вы не определяете эту макрокоманду, то это эквивалентно тому, что Вы определяете ее как пустую команду. Оператор C (без точки с запятой) для вывода в stdio поток STREAM некоторых команд, которые делают метку NAME глобаль- ной, т.е. доступной для ссылки из других файлов. Используйте выражение " аssemble_name (STREAM, NAME) " для вывода самого имени; до и после этого выводится дополнительный синтаксис ассемблера, который делает это имя глобальным и переход на новую строку. Оператор С (без точки с запятой) для вывода в stdio поток STREAM некоторых команд, которые делают метку NAME "сла- бой", то есть доступной для ссылки из других файлов, но ис- пользуемой только тогда, когда нет других доступных опреде- лений. Используйте выражение " assemble_name (STREAM, NAME) " для вывода самого имени; до и после этого выводится дополнительный синтаксис ассемблера, который делает имя "слабым" и переход на новую строку. Если Вы не определяете эту макрокоманду, GNU CC не будет поддерживать "слабые" символы и Вы не должны определять макрокоманду " SUPPORTS_WEAK ". Это выражение C, которое истинно, если поддерживаются "слабые" символы. Если Вы не определяете эту макрокоманду, " defaults.h " дает определение по умолчанию. Если " ASM_WEAKEN_LABEL " определено, определение по умолчанию " 1 "; иначе - " 0 ". Определите эту макрокоманду, если Вы хотите управлять поддержкой слабых символов с флагом транслятора типа " -melf ". Оператор C (без точки с запятой) для вывода в stdio поток STREAM любого текста, необходимого для объявления имени внешнего символа NAME, на который ссылаются при компиляции, но который не определен. Значение DECL - узел дерева для объявления. Эту макрокоманду не обязательно определять, если не требуется ничего выводить.В GNU ассемблере и большинстве Unix ассемблеров ничего не требуется. Оператор C (без точки с запятой) для вывода в STREAM ассемблерного псевдооператора для объявления внешнего имени библиотечной функции. Имя библиотечной функции задается SYMREF, который имеет тип " rtx " и является " symbol_ref ". Эту макрокоманду не обязательно определять, если не требуется ничего выводить.В GNU ассемблере и большинстве Unix ассемблеров ничего не требуется. Оператор C (без точки с запятой) для вывода в stdio поток STREAM ссылки в синтаксисе ассемблера на метку NAME. Это должно добавить " _ " перед именем, если так принято в вашей операционной системе, как в большинстве Berkeley Unix систем. Эта макрокоманда используется в " assemble_name ". Оператор C для вывода в stdio поток STREAM метку, имя которой состоит из строки PREFIX и номера NUM. Крайне необходимо, чтобы эти метки были отличны от меток, используемых для функций и переменных на уровне пользователя. Иначе соответствующие программы будут иметь конфликты с внутренними метками. Желательно исключить внутренние метки из символьной таблицы объектного файла. Большинство ассемблеров имеет соглашение для наименований меток, которые должны быть исключены; во многих системах символ "L" в начале метки имеет этот эффект. Вы должны выяснить, какие соглашения использует ваша система, и следовать этому. Обычное определение этой макрокоманды следующее: fprintf (STREAM, "L%s%d:\n", PREFIX, NUM) Оператор C для сохранения в строке STRING метки, имя которой состоит из строки PREFIX и номера NUM. Эта строка, когда выводится следом за " assemble_name ", должна генерировать вывод, который " ASM_OUTPUT_INTERNAL_LABEL " сгенерировал бы с теми же PREFIX и NUM. Если строка начинается со "*", то " assemble_name " выведет остальную часть строки без изменений. Использование "*" таким образом часто удобно для " ASM_GENERATE_INTERNAL_LABEL ". Если строка не начинается со "*", то " ASM_OUTPUT_LABELREF " может изменить строку при выводе. (Конечно, " ASM_OUTPUT_LABELREF " является также частью вашего машинного описания, так что Вы должны знать то, что она делает на вашей машине.) Оператор C для присваивания OUTVAR (переменной типа " сhar* ") заново распределенной строки, полученной из строки NAME и номера NUMBER, с добавлением некоторой подходящей пунктуации. Используйте " аlloca ", чтобы получить память для строки. Строка будет использоваться как параметр для " ASM_OUTPUT_LABELREF ", чтобы сделать метку ассемблера для внутренней статической переменной NAME. Следовательно, строка должна быть такой, чтобы дать в результате верный код ассемблера. Параметр NUMBER меняется каждый раз, когда эта макрокоманда выполняется; это предотвращает конфликты между одинаково названными внутренними статическими переменными в различных контекстах. В идеале, эта строка не должна быть допустимым C идентификатором, чтобы предотвратить любой конфликт с собственными символами пользователя. Большинство ассемблеров допускают знаки "." или "%" в символах ассемблера; помещения по крайней мере одного из этих знаков между именем и номером будет достаточно. Оператор C для вывода в stdio поток STREAM кода ассемблера, который определяет(присваивает) символу NAME значение VALUE. Если SET_ASM_OP определен, обеспечивается определение по умолчанию, которое корректно для большинства систем. Определите эту макрокоманду, чтобы изменить заданные по умолчанию ассемблерные имена, используемые для методов Objective C. Заданное по умолчанию имя - уникальный номер метода, сопровождаемый именем класса (например, " _1_Foo "). Для методов в категориях имя категории также включается в имя ассемблера (например, " _1_Foo_Bar "). Эти имена безопасны в большинстве систем, но создают трудности при отладке, так как селектор метода не представлен в имени. Поэтому отдельные системы определяют другие способы вычисления имен. BUF - выражение типа " char * ", которое дает Вам буфер, в котором хранится имя; его длина равна суммарной длине CLASS_NA- ME, CAT_NAME и SEL_NAME плюс 50 символов. Параметр IS_INST определяет, является ли метод экземпляром метода или классом метода; CLASS_NAME - имя класса; CAT_NAME - имя категории (или NULL, если метод не содержится в категории); и SEL_NAME - имя селектора. На системах, где ассемблер может обрабатывать имена в кавычках, Вы можете использовать эту макрокоманду, чтобы обеспечить более читаемые имена. 6.16.5. Как обрабатываются функции инициализации Оттранслированная программа для некоторых языков включает "конструкторы" (называемые также " подпрограммами инициализации ") - функции для инициализации данных программы, когда она начинает работу. Эти функции должны быть вызваны прежде, чем программа "запускается" - то есть перед вызовом " main " Компилирование некоторых языков генерирует "деструкторы" (называемые также " подпрограммами завершения "), которые должны вызываться, когда программа завершает работу. Чтобы заставить работать функции инициализации и завершения, компилятор должен вывести кое-что в коде ассемблера, чтобы эти функции вызывались в соответствующее время. Когда Вы адаптируете транслятор к новой системе, Вы должен указать, как сделать это. Имеются два основных способа, которыми GCC поддерживает выполнение функций инициализации и завершения. Каждый способ имеет два варианта. Многое из структуры является общим для всех четырех вариантов. Компоновщик должен формировать два списка из этих функций - список функций инициализации, называемый " __ CTOR_LIST __ ", и список функций завершения, называемый " __ DTOR_LIST __ ". Каждый список всегда начинается с игнорируемого указателя на функцию (который может содержать 0, -1 или количество указателей на функцию после него, в зависимости от среды). За ним идет последовательность из нуля или более функций - указателей на конструкторы (или деструкторы), заканчивающаяся указателем на функцию, содержащим ноль. В зависимости от операционной системы и формата исполняемого файла, " crtstuff.c " или " libgcc2.c " просматривает эти списки в момент запуска и выхода. Конструкторы вызваются в порядке, обратном порядку в списке; деструкторы - в прямом порядке. Самый лучший способ обработки статических конструкторов работает только для форматов объектного файла, которые поддерживают произвольно названные разделы. Разделы определяются отдельно для списка конструкторов и для списка деструкторов.Традиционно они называются ".ctors " и ".dtors ". Каждый объектный файл, который определяет функцию инициализации, также помещает слово в раздел конструктора, чтобы указать на эту функцию. Компоновщик собирает все эти слова в один ".ctors" раздел. Функции завершения обрабатываются аналогично. Чтобы использовать этот метод, необходимо определить макрокоманды " ASM_OUTPUT_CONSTRUCTOR " и " ASM_OUTPUT_DESTRUCTOR ". Обычно можно получить их, включая " svr4.h ". Если доступны произвольные разделы, имеется два варианта, в зависимости от того, как вызывается код в " crtstuff.c ". В системах, которые поддерживают "init" раздел, который выполняется при запуске программы, части " crtstuff.c " компилируются в этот раздел. Программа, компонуется " gcc " драйвером примерно так: ld -o OUTPUT_FILE crtbegin.o ... crtend.o -lgcc Заголовок функции (" __ do_global_ctors ") появляется в init разделе " crtbegin.o "; остаток функции появляется в init разделе " crtend.o ". Компоновщик соединит две эти части раздела вместе, создавая целую функцию. Если какие-то из пользовательских объектных файлов скомпоновались в середину этого кода, то этот код будет выполняться как часть тела " __ do_global_ctors ". Чтобы использовать этот вариант, нужно соответственно определить макрокоманду " INIT_SECTION_ASM_OP ". Если никакой init раздел не доступен, " INIT_SECTION_ASM_OP " не определяется. Затем " __ do_global_ctors" встраивается в текст раздела подобно всем другим функциям и располагается в " libgcc.a ". Когда GCC компилирует произвольную функцию, называемую " main ", он вставляет вызов процедуры " __main " как первый выполнимый код после пролога функции. Функция " __main ", также определенная в " libgcc2.c ", просто вызывает " __ do_global_ctors ". В форматах файла, которые не поддерживают произвольные разделы, cнова имеется два варианта. В самом простом варианте должны использоваться GNU компоновщик (GNU " ld ") и формат " a.out ". В этом случае " ASM_OUTPUT_CONSTRUCTOR " определяется так, чтобы производить ".stabs" вход типа " N_SETT ", ссылаясь на имя " __CTOR_LIST__ ", и с адресом void функции, содержащей код инициализации как значение. GNU компоновщик распознает это как запрос, чтобы добавить значение к "set"; значения накапливаются и,в конечном счете, помещаются в выполняемый файл как вектор в формате, описанном выше, со счетчиком (игнорируемым) в начале и конечным элементом ноль. " ASM_OUTPUT_DESTRUCTOR " обрабатываеся аналогично. Так как нет доступного init раздела, отсутствие " INIT_SECTION_ASM_OP " заставляет трансляцию " main " вызывать "__ main " как выше, начиная процесс инициализации. Последний вариант не использует ни произвольные разделы, ни GNU компоновщик. Он предпочтителен, когда требуется динамическая компоновка и когда используются форматы файла, которые GNU компоновщик не поддерживает, типа " ECOFF ". В этом случае, " ASM_OUTPUT_CONSTRUCTOR " не генерирует " N_SETT " символ; функции инициализации и завершения распознаются просто по своим именам. Это требует дополнительной программы на шаге компоновки, которая называется " collect2 ". Эта программа эмулирует компоновщик для использования GNU CC; она работает, запуская обычным компоновщиком, но также упорядочивает включение векторов функций инициализации и завершения. Эти функции вызываются через " __ main ", как описано выше. Выбор из этих опций конфигурации упрощается набором файлов, зависимых от операционной системы, в каталоге " config ". Эти файлы определяют все существенные параметры. Обычно достаточно включать один из них в файл машинно-зависимой конфигурации. Это следующие файлы: Для операционных систем, использующих формат ` a.out '. Для операционных систем, использующих формат ` MachO '. Для Системы V выпуска 3 и подобных систем, использующих формат ` COFF '. Для Системы V выпуска 4 и подобных систем, использующих формат ` ELF '. Для операционной системы VMS. Следующий раздел описывает конкретные макрокоманды, которые управляют и настраивают обработку функций завершения и инициализации. 6.16.6. Макрокоманды, управляющие программами инициализации Вот макрокоманды, управляющие обработкой транслятором функций инициализации и завершения: Если она определена, это строковая константа C для операции ассемблера, чтобы идентифицировать следующие данные как код инициализации. Если не определена, GNU CC считает, что такого раздела не существует. Когда Вы используете специальные разделы для функций инициализации и завершения, эта макрокоманда также управляет тем, как " crtstuff.c " и " Libgcc2.c " приспосабливаются для запуска функций инициализации. Если она определена, " main " не будет вызывать " __ main ", как описано выше. Эта макрокоманда должна определяться для систем, которые управляют содержанием init раздела на посимвольной основе, типа OSF/1, и не должна определяться явно для систем ,которые поддерживают " INIT_SECTION_ASM_OP ". Если определена, это строковая константа С для переключателя, который сообщает компоновщику, что следующий символ - подпрограмма инициализации. Если определена, это строковая константа С для переключателя, который сообщает компоновщику, что следующий символ - подпрограмма завершения. Если определено, " main " будет вызывать " __main ", несмотря на присутствие " INIT_SECTION_ASM_OP ". Эта макрокоманда должна быть определена для систем, где init раздел фактически не выполняется автоматически, но полезен для составления списков конструкторов и деструкторов. Эта макрокоманда определяется как оператор C для вывода в поток STREAM кода ассемблера для приспособления вызова функции NAME во время инициализации. Предположим, что NAME - имя функции C, сгенерированной автоматически транслятором. Эта функция не имеет параметров. Используйте функцию " assemble_name " для вывода имени NAME; она выполняет любые специфические для системы синтаксические преобразования, такие как добавление подчеркивания. Если Вы не определяете эту макрокоманду, ничего специального не выводится для упорядочивания вызова функции. Это корректно, когда функция вызывается другим способом - например, посредством программы " collect2 ", которая просматривает таблицу символов, чтобы найти эти функции по их именам. Похоже на " ASM_OUTPUT_CONSTRUCTOR ", но используется скорее для функций завершения чем функций инициализации. Если ваша система использует " collect2 " как средство обработки конструкторов, то эта программа обычно использует " nm ", для поиска в объектном файле функций конструктора, которые нужно вызвать. На некоторых видах систем эти макрокоманды могут определяться, чтобы выполнять работу " collect2 " быстрее (и, в некоторых случаях, заставить ее работать вообще): Определите эту макрокоманду, если система использует формат COFF (Common Object File Format) объектных файлов, чтобы " collect2 " мог распознать этот формат и просматривать объектные файлы непосредственно для поиска динамических функций конструктора/деструктора. Определите эту макрокоманду, если система использует формат ROSE объектных файлов, чтобы " collect2 " мог распознать этот формат и просматривать объектные файлы непосредственно для поиска динамических функций конструктора/деструктора. Эти макрокоманды имеют эффект только для "родного" компилятора; " collect2 " как часть кросс-компилятора всегда использует " nm " для целевой машины. Определите эту макракоманду как строковую константу С, содержащую имя файла, используемого, чтобы выполнить " nm ". По умолчанию ищется путь, обычный для " nm ". Если система поддерживает общедоступнные библиотеки и имеет программу, чтобы внести в список динамические зависимости данной библиотеки или выполнимой программы, можно определить эти макрокоманды, чтобы разрешить поддержку запуска функций инициализации и завершения в общедоступнных библиотеках: Эта макрокоманда определяется как строковая константа C, содержащая имя программы, которая вносит в список динамические зависимости, такие как " "ldd" " под SunOS 4. Эта макрокоманда определяется как C код, который извлекает имена файлов из вывода программы, обозначенной " LDD_SUFFIX ". PTR - переменная типа " char * ", которая указывает на начало строки вывода из " LDD_SUFFIX ". Если строка вносит в список динамическую зависимость, код должен передвинуть PTR к началу имени файла на этой строке. Иначе он должен установить PTR на " NULL ". 6.16.7. Вывод Команд Ассемблера Здесь описывается вывод команд ассемблера. Инициализатор С, содержащий ассемблерные имена для машинных регистров, каждое из которых - строковая константа C. Она транслирует количество регистров в трансляторе в язык ассемблера. Если определено,то это C инициализатор для массива структур, содержащих имя и номер регистра. Эта макрокоманда определяет дополнительные имена для аппаратных регистров, таким образом разрешая опцию " asm " в объявлениях для ссылки на регистры с использованием альтернативных имен. Определите эту макрокоманду, если используется нестандартный ассемблер, который требует различные имена для машинных команд. Определение - оператор или операторы C, которые выводят код операции команды ассемблера в stdio поток STREAM. Операнд PTR - переменная типа " char * ", которая указывает на имя кода операции во "внутренней" форме - форме, которая описана в машинном описании. Это определение должно вывести имя кода операции в STREAM, выполняя любую нужную трансляцию, и увеличить переменную PTR, чтобы она указывала на конец кода операции, чтобы он не выводился дважды. Фактически,ваше определение макрокоманды может обрабатывать меньше или больше, чем все имя кода операции; но если Вы хотите обработать текст, который включает "%"-последовательности для подстановки операндов, Вы должны позаботиться о подстановке самостоятельно. Вам только надо быть уверенными, что PTR увеличивается для любого текста, который не должен выводиться нормально. Если надо рассмотреть значения операнда, они могут быть найдены как элементы " recog_operand ". Если макрокоманда ничего не определяет, команда выводится обычным способом. Если определено, это оператор C, который будет выполнен прямо перед выводом кода ассемблера для INSN, чтобы изменить извлеченные операнды так, чтобы они выводились по-разному. Параметром OPVEC является вектор, содержащий операнды, взятые из INSN, и NOPERANDS - число элементов вектора, которые содержат значимые данные для insn. Содержание этого вектора - то, что будет использоваться, для преобразования шаблона insn в код ассемблера, так что можно изменять ассемблерный вывод, изменяя содержание вектора. Эта макрокоманда полезна, когда разные синтаксисы ассемблера используют один файл образцов инструкций; определяя эту мак- рокоманду иначе, можно заставить большой класс команд выводиться по-разному (например, с переставленными операндами). Конечно, изменения в синтаксисе ассемблера, воздействующие на индивидуальные образцы insn, должны быть обработаны при записи условных программ вывода в этих образцах. Если эта макрокоманда не определена, она эквивалентна пустому оператору. Составной операторы С, чтобы вывести в stdio поток STREAM синтаксис ассемблера для операнда команды X. X - выражение RTL. CODE - значение, которое может использоваться для определения одного из нескольких способов печати операнда. Используется, когда идентичные операнды должны печататься по-разному в зависимости от контекста. CODE исходит из "%" спецификации, которая использовалась, чтобы запросить печать операнда. Если спецификация была только " %DIGIT ", то CODE - 0; Если спецификация была " %LTR DIGIT ", то CODE - ASCII код для LTR. Если X - регистр, эта макрокоманда напечатает имя регистра. Имена могут быть найдены в массиве " reg_names ", тип которого - "char *[] ". " reg_names " инициализируется из " REGISTER_NAMES ". Когда машинное описание имеет спецификацию " %PUNCT " (за "%" следует символ пунктуации), эта макрокоманда вызывается с нулевым указателем для X и символом пунктуации для CODE. Выражение C, которое истинно, если CODE - допустимый символ пунктуации для использования в макрокоманде " PRINT_OPERAND ". Если " PRINT_OPERAND_PUNCT_VALID_P " не определена, это означает, что символы пунктуации (кроме стандартного, "%") не используются таким образом. Составной оператор C для вывода в stdio поток STREAM синтаксиса ассемблера для операнда команды, который является ссылкой памяти, чей адрес - X. X - выражение RTL. На некоторых машинах синтаксис для символического адреса зависит от раздел, на который адрес ссылается. На этих машинах определите макрокоманду " ENCODE_SECTION_INFO ", чтобы сохранить информацию в " symbol_ref ", и затем проверяют ее здесь. * См.: Формат Ассемблера::. Оператор C, который должен быть выполнен после того, как были выведены все инструкции заполнения слотов. В случае необходимости вызовите "dbr_sequence_length ", чтобы определить число слотов, заполненных последовательно (ноль, если в настоящее время не выводится последовательность), чтобы решить, сколько вывести пустых команд. Не определяйте эту макрокоманду, если ей нечего делать, но она полезна при чтении вывода, если размер последовательности задержек указан явно (например с пропуском). Обратите внимание, что программы вывода для команд со слотами задержки должны быть подготовлены, чтобы иметь дело не обязательно с частью последовательности (то есть когда проход планирования не запускался, или когда заполнители слотов не были найдены). Переменная " final_sequence " равна нулю, если последовательность не обрабатывается, иначе она содержит выводимую " sequence " rtx. Если определены, то это строковые выражения С для использования для " %R ", " %L "," %U ", и " %I " опций " asm_fprintf " (см. " final.c "). Они полезны, когда одиночный " md " файл должен поддерживать несколько форматов ассемблера. В этом случае различные " tm.h " файлы могут определять эти макрокоманды по разному. Если целевая машина поддерживает различные варианты языка ассемблера, (такие, как различные коды операций), определите эту макрокоманду как выражение C, которое дает числовой индекс варианта языка ассемблера для использования, ноль - первый вариант. Если эта макрокоманда определена, Вы можете использовать конструкции " {option0|option1|option2...} " в шаблонах вывода образцов (*См.: Шаблоны Вывода::.) или в первом параметре " asm_fprintf ". Эта конструкция выводит " option0 ", " option1 " или " оption2 ", и т.д., если значение " ASSEMBLER_DIALECT " равно нулю, один или два, и т.д. Все специальные символы внутри этих строк сохраняют свое обычное значение. Если Вы не определяете эту макрокоманду, символы " {", " | " и "} " не имеют никакого специального значения, когда они используются в шаблонах или операндах для " asm_fprintf ". Определите макрокоманды " REGISTER_PREFIX ", " LOCAL_LABEL_PREFIX ", " USER_LABEL_PREFIX " и " IMMEDIATE_PREFIX ", если Вы можете выразить изменения в синтаксисе языка ассемблера этим способом. Определите " ASSEMBLER_DIALECT " и используйте " {option0|option1} " синтаксис, если вариант синтаксиса больше и включает такие вещи, как различные коды операций или порядок операндов. Выражение C для вывода в STREAM некоторого кода ассемблера, который поместит аппаратный регистр номер REGNO в стек. Код не обязательно должен быть оптимальным, так как эта макрокоманда используется только при профилировании. Выражение C для вывода в STREAM некоторого кода ассемблера, который восстановит аппаратный регистр номер REGNO из стека. Код не обязательно должен быть оптимальным, так как эта макрокоманда используется только при профилировании. 6.16.8. Вывод Таблиц Распределения Это касается таблиц распределения. Эту макрокоманду нужно определить на машинах, где адреса в таблицах распределения абсолютные. Определение должно быть оператором C для вывода в stdio поток STREAM псевдоинструкции ассемблера для генерации различия между двумя метками. VALUE и REL - номера двух внутренних меток. Определения этих меток выводятся с использованием " ASM_OUTPUT_INTERNAL_LABEL ", и они должны печататься здесь таким же образом. Например, fprintf (STREAM, "\t.word L%d-L%d\n", VALUE, REL) Эту макрокоманду нужно определить на машинах, где адреса в таблицах распределения абсолютны. Определение должно быть оператором C для вывода в stdio поток STREAM псевдо-инструкции ассемблера для генерации ссылки на метку. VALUE - номер внутренней метки, определение которой выводится с использованием " ASM_OUTPUT_INTERNAL_LABEL ". Например, fprintf (STREAM, "\t.word L%d\n", VALUE) Определите эту макрокоманду, если метка перед таблицей перехода должна выводиться отдельно. Первые три параметра - такие же, как для " ASM_OUTPUT_INTERNAL_LABEL "; четвертый параметр - таблица перехода, которая следует за ними(" jump_insn ", содержащий " addr_vec " или " addr_diff_vec "). Эта особенность используется на системе V, для вывода оператора " swbeg " для таблицы. Если эта макрокоманда не определена, эти метки выводятся " ASM_OUTPUT_INTERNAL_LABEL ". Определите это, если что-нибудь специальное должно выводиться в конце таблицы перехода. Определением должен быть оператор C, который будет выполнен после того, как будет записан код ассемблера для таблицы. Оператор должен писать соответствующий код в stdio поток STREAM. Параметр TABLE является таблицей перехода insn, и NUM - номер предыдущей метки. Если эта макрокоманда не определена, в конце таблицы перехода ничего специального не выводится. 6.16.9. Команды Ассемблера для Выравнивания Здесь описываются команды для выравнивания. Выражение C для вывода текста, чтобы выровнять текущий счетчик способом, который является желательным в участке кода, который может быть достигнут только при помощи перехода. Эту макрокоманду не обязательно определять, если в данный момент не нужно никакого специального выравнивания. Большинство машинных описаний машины сейчас не определяет эту макрокоманду. Выражение C, для вывода текста, чтобы выровнять текущий счетчик, который является желательным в начале цикла. Эту макрокоманду не обязательно определять, если в данный момент не нужно никакого специального выравнивания. Большинство машинных описаний машины сейчас не определяет эту макрокоманду. Оператор C для вывода в stdio поток STREAM команды ассемблера, чтобы увеличить расположение счетчика ячеек на NBYTES байт. Эти байты должны равняться нулю при загрузке. NBYTES - выражение C типа " int ". Определите эту макрокоманду, если " ASM_OUTPUT_SKIP " не будет использоваться в текстовом разделе, потому что она не может поместить нули в байты, которые пропущены. Это так во многих Unix системах, где псевдооператоры пропуска байтов генерируют скорее пустые команды, чем нули, когда они используются в текстовом разделе. Оператор C, служащий для вывода в stdio поток STREAM команды ассемблера для того, чтобы увеличить счетчик ячеек до кратного 2 в степени POWER байт. POWER должно быть выражением C типа " int ". 6.17. Управление Информационным Форматом отладки Здесь описывается, как определять отладочную информацию. 6.17.1. Макрокоманды, которые воздействуют на все отладочные форматы Эти макрокоманды воздействуют на все отладочные форматы. Выражение C, которое возвращает номер регистра DBX для номера регистра транслятора REGNO. В простейших случаях значение этого выражения может быть самим REGNO. Но иногда имеются регистры, о которых транслятор знает, а DBX - нет, или наоборот. В таких случаях некоторые регистры должны иметь один номер для транслятора и другой - для DBX. Если два регистра имеют последовательные номера внутри GNU CC, и они могут использоваться как пара, чтобы содержать значение из нескольких слов, то они *должны* иметь последовательные номера после перенумерации " DBX_REGISTER_NUMBER ". Иначе, отладчики не смогут обратиться к такой паре, потому что они ожидают, что регистровые пары будут последовательными в их собственной схеме нумерации. Если Вы определяете " DBX_REGISTER_NUMBER " способом, который не сохраняет пары регистров, то Вы должны вместо этого переопределить фактическую схему нумерации регистров. Выражение C, которое возвращает целочисленное значение смещения для автоматической переменной, имеющей адрес X (выражение RTL). Вычисление по умолчанию предполагает, что X отсчитывается от указателя кадра и дает смещение от указателя кадра. Это требуется для целевых машин, которые производят вывод отладки для DBX или COFF-форматный вывод отладки для SDB и позволяет удалять указатель кадра, когда используются " -g " опции. Выражение C, которое возвращает целочисленное значение смещения для параметра, имеющего адрес X (выражение RTL). Номинальное смещение - OFFSET. Выражение C, которое возвращает тип вывода отладки, который GNU CC производит, если пользователь указывает " -g " или " -ggdb ". Определите его, если Вы приспособили GNU CC, чтобы поддерживать более чем один формат отладки вывода. В настоящее время допустимые значения - " DBX_DEBUG "," SDB_DEBUG ", " DWARF_DEBUG " и " XCOFF_DEBUG ". Значение этой макрокоманды воздействует только на отладочный вывод по умолчанию; пользователь может всегда получать конкретный тип вывода, используя " -gstabs ", " -gcoff ", " -gdwarf ", или " -gxcoff ". 6.17.2. Специфические Опции для Вывода DBX Вот специфические опции для вывода DBX. Определите эту макрокоманду, если GNU CC произвел вывод отладки для DBX в ответ на опцию ` -g '. Определите эту макрокоманду, если GNU CC произвел вывод отладки формата XCOFF в ответ на опцию ` -g '. Это - вариант DBX формата. Определите эту макрокоманду, чтобы контролировать, будет ли GNU CC по умолчанию генерировать расширенную версию GDB об отладочной информации DBX (считая, что отладочная информация DBX-формата вообще разрешена). Если Вы не определяете эту макрокоманду, значение по умолчанию - 1: всегда генерируется расширенную информацию, если для этого имеется возможность. Определите эту макрокоманду, если все ".stabs " команды должны быть выведены в текстовом разделе. Строковая константа С, содержащая имя ассемблерного псевдооператора, которое можно использовать вместо ".stabs", чтобы определить обычный символ отладки. Если Вы не определяете эту макрокоманду, используется ".stabs". Эта макрокоманда применяется только для DBX-формата информации об отладке. Строковая константа С, содержащая имя ассемблерного псевдооператора, которое можно использовать вместо ".stabd", чтобы определить отладочный символ,значением которого является текущее расположение. Если Вы не определяете эту макрокоманду, используется ".stabd". Эта макрокоманда применяется только для DBX-формата информации об отладке. Строковая константа С, содержащая имя ассемблерного псевдооператора, которое можно использовать вместо ".stabn", чтобы определить безымянный отладочный символ. Если Вы не определяете эту макрокоманду, используется ".stabn". Эта макрокоманда применяется только для DBX-формата информации об отладке. Определите эту макрокоманду, если DBX на вашей системе не поддерживает конструкцию " xsTAGNAME ". В некоторых системах эта конструкция используется для описания прямой ссылки на структуру, называемую TAGNAME. В других системах, эта конструкция не поддерживается вообще. Символьное имя в информации об отладке DBX-формата обычно продолжается (расщепляясь на две отдельных ".stabs" директивы), когда оно превышает соответствующую длину (по умолчанию 80 символов). В некоторых операционных системах DBX требует этого расщепления; в других расщепление не должно выполняться. Вы можете запретить расщепление определением этой макрокоманды значением ноль. Вы можете изменить заданную по умолчанию длину расщепления, определяя эту макрокоманду как выражение для длины, которая Вам требуется. Обычно продолжение определяется добавлением символа "\" к концу ".stabs" строки, когда продолжение следует.Для использования вместо него другого символа, определите эту макрокоманду как символьную константу для символа, который Вы хотите использовать. Не определяйте эту макрокоманду, если наклонная черта влево подходит для вашей системы. Определите эту макрокоманду, если необходимо перейти к разделу данных перед выведением ".stabs" псевдооператора для неглобальной статической переменной. Значение для использования в поле "code" директивы ".stabs" для определения типа. Значение по умолчанию - " N_LSYM ". Значение для использования в поле "code" директивы ".stabs" для статической переменной, размещаемой в текстовом разделе. Формат DBX не обеспечивает никакого "правильный" способа сделать это. Значение по умолчанию - " N_FUN ". Значение для использования в поле "code" директивы ".stabs" для параметра, передаваемого в регистрах. Формат DBX не обеспечивает никакого "правильного" способа сделать это. Значение по умолчанию - " N_RSYM ". Буква для использования в символьных данных DBX для идентификации символа как параметра, передаваемого в регистрах. Формат DBX обычно не обеспечивает какого-нибудь способа сделать это. Значение по умолчанию - " "P". Буква для использования в символьных данных DBX для идентификации символа как параметра стека. Значение по умолчанию - " "p" ". Определите эту макрокоманду, если DBX информация для функции и ее аргументов должна предшествовать коду ассемблера для этой функции. Обычно, в формате DBX, информация об отладке полностью следует за кодом ассемблера. Определите эту макрокоманду, если " N_LBRAC " символ для блока должен предшествовать информации об отладке для переменных и функций, определенных в этом блоке. Обычно, в формате DBX, " N_LBRAC " символ идет первым. Определите эту макрокоманду если значение символа, описывающего область блока (" N_LBRAC " или " N_RBRAC "), должно быть относительно начала вложенной функции. Обычно GNU C использует абсолютный адрес. 6.17.3. Открытые Ловушки для Формата DBX Вот ловушки для формата DBX. Определите эту макрокоманду, чтобы сказать, как выводить в STREAM отладочную информацию для начала области для имен переменных. Параметр NAME - это имя символа ассемблера (для использования с " assemble_name "), значение которого - адрес начала области. Подобно " DBX_OUTPUT_LBRAC ", но для конца области. Определите эту макрокоманду, если целевая машина требует специальной обработки для вывода типа перечисления. Это определение должно быть C оператором (без точки с запятой) для вывода соответствующей информации в STREAM для типа TYPE. Определите эту макрокоманду, если целевая машина требует специального вывода в конце информации об отладке для функции. Определение должно быть оператором C (без точки с запятой) для вывода приспособленной информации в STREAM. FUNCTION - это " FUNCTION_DECL " узел для функции. Определите эту макрокоманду, если вам нужно контролировать порядок вывода стандартных типов данных в начале компиляции. Параметр SYMS - "дерево", которое является цепочкой всей предопределенных глобальных символов, включая имена типов данных. Обычно DBX вывод начинается с определений типов для целых чисел и символов, дальше следуют все другие предопределенные типы специфического языка, но не в специфическом порядке. На некоторых машинах, необходимо сначала вывести различные конкретные типы. Чтобы сделать это, определите " DBX_OUTPUT_STANDARD_TYPES ", чтобы она выводила эти символы в необходимом порядке. Все предопределенные типы, которые Вы явно не выводите, будут выводиться позже без специфического порядка. Будьте внимательным, чтобы не определить эту макрокоманду так, чтобы она работала только для C. Нет никаких глобальных переменных для доступа к большинству встроенных типов, потому что другой язык может иметь другой набор типов. Способ вывода данного типа состоит в том, чтобы просмотреть SYMS, чтобы увидеть, можете ли Вы его найти. Вот пример: { tree decl; for (decl = syms; decl; decl = TREE_CHAIN (decl)) if (!strcmp (IDENTIFIER_POINTER (DECL_NAME (decl)), "long int")) dbxout_symbol (decl); ... } Эта программа ничего не делает, если ожидаемый тип не существует. См. функцию " init_decl_processing " в " c-decl.c ", чтобы найти имена, используемые для всех встроенных типов C. Вотдругой путь нахождения данного типа: { tree decl; for (decl = syms; decl; decl = TREE_CHAIN (decl)) if (TREE_CODE (decl) == TYPE_DECL && (TREE_CODE (TREE_TYPE (decl)) == INTEGER_CST) && TYPE_PRECISION (TREE_TYPE (decl)) == 16 && TYPE_UNSIGNED (TREE_TYPE (decl))) /* This must be `unsigned short'. */ dbxout_symbol (decl); ... } 6.17.4. Имена Файлов в Формате DBX Здесь описываются имена файла в формате DBX. Определите это, если DBX хочет, чтобы текущий каталог был записан в каждый объектный файл. Обратите внимание, что рабочий каталог всегда записывается, если включены расширения GDB. Оператор C для вывода DBX информации об отладке в stdio поток STREAM, который определяет, что файл NAME - основной файл источника - этот файл указывается как входной файл для компиляции. Эта макрокоманда вызывается только однажды, в начале трансляции. Эта макрокоманда не нуждается в определении, если стандартная форма вывода для DBX отладочной информации является подходящей. Оператор C для выводя DBX информации об отладке в stdio поток STREAM, который указывает, что текущий каталог в течение трансляции называется NAME. Эта макрокоманда не нуждается в определении, если стандартная форма вывода для DBX отладочной информации является подходящей. Оператор C для вывода DBX информации об отладке в конец трансляции основного файла источника NAME. Если Вы не определяете эту макрокоманду, ничего специального не выводится в конце трансляции, что является правильным для большинства машин. Оператор C для вывода DBX информации об отладке в stdio поток STREAM, который определяет, что файла NAME - текущий файл источника. Этот вывод генерируется каждый раз, когда ввод перемещается на другой исходный файл в результате " #include ", конца включенного файла или команды " #line ". Эта макрокоманда не нуждается в определении, если стандартная форма вывода для DBX отладочной информации является подходящей. 6.17.5. Макрокоманды для SDB и Вывода DWARF Вот макрокоманды для вывода DWARF и SDB. Определите эту макрокоманду, если GNU CC должен вывести вывод отладки в coff-формате для SDB в ответ на опцию ` -g '. Определите эту макрокоманду, если GNU CC должен вывести вывод отладки в DWARF формате в ответ на опцию ` -g '. Определите эти макрокоманды, чтобы изменить синтаксис ассемблера для специальных SDB директив ассемблера. См. " sdbout.c " для списка этих макрокоманд и их параметров. Если используетя стандартный синтаксис, Вам не нужно определять их самостоятельно. Некоторые ассемблеры не поддерживают точку с запятой как разделитель, даже между SDB директивами ассемблера. В этом случае определите эту макрокоманду как используемый разделитель (обычно " \n "). Не обязательно определять новый набор " PUT_SDB_OP " макрокоманд, если это - единственое требуемое изменение. Определите эту макрокоманду, чтобы изменить обычный метод построения dummy имени для анонимной структуры и типов объединения. См. " sdbout.c " для большей информации. Определите эту макрокоманду, чтобы разрешить выделение ссылок на структуры, объединения и перечисления, которые еще не встечались. Стандарт COFF не позволяет обработку неизвестных ссылок, а MIPS ECOFF поддерживает их. Определите эту макрокоманду, чтобы разрешить обработку ссылок на структуры, объединения и перечисления, которые еще не встечались. Некоторые ассемблеры не поддерживают забегания вперед, в то время как некоторые требуют его. 6.18. Кросс-трансляция и числа с плавающей точкой В то время как все современные машины используют одинаковое представление для целых чисел, существует множество представлений чисел с плавающей точкой. Это значит, что при перекрестной трансляции представление чисел с плавающей точкой (вещественных чисел) в компилируемой программе может быть отлично от представления на машине, на которой происходит трансляция. Т.к. различные системы представлений могут использовать числа с разными размерами и разной точность, то перекрестная компиляция не может использовать вещественную арифметику главной машины. Следовательно, вещественные константы должны представляться в формате целевой машины. Это означает, что в фазе перекрестной компиляций нельзя использовать `atof' для анализирования вещественных констант. Вместо этого должна использоваться специальная подпрограмма. Свертка констант так же должна эмулировать арифметику целевой машины (или свертка констант не должна производиться вообще). Макрокоманды из следующей таблицы должны быть определены только если Вы производите перекрестную компиляцию между машинами с различными форматами чисел с плавающей точкой. Иначе, не определяйте их совсем. В этом случае будут установлены значения по умолчанию, использующие тип данных `double' и `==' для проверки равенства, и т.д. Вам не нужно беспокоиться, как много раз Вы используете операнд любой из этих макрокоманд. Транслятор никогда не использует операнды которые имеют побочный эффект. `REAL_VALUE_TYPE' Макрокоманда для типа данных C, который нужно использовать, чтобы держать вещественное (с плавающей точкой) значение в формате целевой машины. Обычно этот тип должен быть `struct', содержащая массив `int'. `REAL_VALUES_EQUAL (X, Y)' Макрокоманда для выражения на C, которое проверяет равенство двух значения, X и Y, оба типа `REAL_VALUE_TYPE'. `REAL_VALUES_LESS (X, Y)' Макрокоманда для выражения на С, которое проверяет является ли X меньше чем Y, оба значения типа `REAL_VALUE_TYPE' и интерпретируются как числа с плавающей точкой в формате целевой машины. `REAL_VALUE_LDEXP (X, SCALE)' Макрокоманда для выражения на C, которое выполняет стандартную библиотечную функцию `ldexp', но использует представление плавающей точки в формате целевой машины. Как X, так и значение выражения имеют тип `REAL_VALUE_TYPE'. Второй параметр, SCALE - целое число. `REAL_VALUE_FIX (X)' Макрокоманда, которая определяется, как выражение на С, преобразующее значение Х в вещественном формате целевой машины к целому знаковому числу. Х имеет тип `REAL_VALUE_TYPE'. `REAL_VALUE_UNSIGNED_FIX (X)' Макрокоманда, которая определяется, как выражение на С, преобразующее значение Х в вещественном формате целевой машины к целому беззнаковому числу. Х имеет тип `REAL_VALUE_TYPE'. `REAL_VALUE_RNDZINT (X)' Макрокоманда, которая определяется, как выражение на С, округляющее значение Х в вещественном формате целевой машины в сторону нуля к целому числу (но при этом тип остается вещественным). Х имеет тип `REAL_VALUE_TYPE'. `REAL_VALUE_UNSIGNED_RNDZINT (X)' Макрокоманда, которая определяется, как выражение на С, округляющее значение Х в вещественном формате целевой машины в сторону нуля к целому беззнаковому числу (но при этом тип остается вещественным). Х имеет тип `REAL_VALUE_TYPE'. `REAL_VALUE_ATOF (STRING, MODE)' Макрокоманда, для выражения на С, которое преобразует выражение типа char* - STRING в вещественное число в формате целевой машины, представленное типом MODE. Х имеет тип `REAL_VALUE_TYPE'. `REAL_INFINITY' Определите эту макрокоманду, если возможно бесконечное вещественно значение. В таком случае деление на ноль является корректной операцией. `REAL_VALUE_ISINF (X)' Макрокоманда, для выражения на С, которое определяет является ли вещественное число Х бесконечностью. Значение имеет тип `int'. По умолчанию она вызывает `isinf'. `REAL_VALUE_ISNAN (X)' Макрокоманда, для выражения на С, которое определяет является ли вещественное число "nan" (not-a-number [не число]). Значение имеет тип `int'. По умолчанию, это определено, чтобы вызвать `isnan'. Определите следующие дополнительные макрокоманды, если Вы хотите чтобы во время перекрестной трансляции происходило сворачивание вещественных констант. Если Вы их не определите, то перекрестная трансляция все еще возможна, но свертывания вещественных константы не будет. `REAL_ARITHMETIC (OUTPUT, CODE, X, Y)' Макрокоманда, для выражения на С, которое вычисляет результат арифметической операции над над двумя значениями с плавающей точкой. X и Y типа `REAL_VALUE_TYPE`, представленного в формате целевой машины. Результат будет иметь тот же тип и такое же представление типа. Результат сохраняется в OUTPUT (OUTPUT должен быть переменной). Операция, которую нужно выполнить определяется CODE - кодом, который всегда один из следующих: `PLUS_EXPR', `MINUS_EXPR', `MULT_EXPR', `RDIV_EXPR', `MAX_EXPR' или `MIN_EXPR'. Расширение этой макрокоманды ответственно за проверку переполнения. Если случается переполнение, макрорасширение должно выполнить оператор `return 0;', который указывает на неспособность выполнить запрошенную арифметическую операцию. `REAL_VALUE_NEGATE (X)' Макрокоманда, для выражения на С, которое возвращает вещественное значение Х с измененным знаком. Как Х, так и значение выражения имеют тип `REAL_VALUE_TYPE' и представлены в формате целевой машины. В этой макрокоманде не предусмотрен механизм сообщения о переполнение, т.к. переполнение невозможно. `REAL_VALUE_TRUNCATE (MODE, X)' Макрокоманда, для выражения на С, которое преобразовывает вещественное значение Х в тип MODE. Как Х, так и значение выражения представлены в формате целевой машины и имеют тип `REAL_VALUE_TYPE'. Тем не менее, значение должно иметь соответствующий битовый образец, чтобы его можно было корректно выводить как плавающую константу, чей тип в точности согласуется с типом MODE. В этой макрокоманде не предусмотрен механизм сообщения о переполнение. `REAL_VALUE_TO_INT (LOW, HIGH, X)' Макрокоманда, для выражения на С, которое преобразовывает вещественное число Х в целое число двойной точности, которое затем сохраняется в двух переменных типа INT - LOW и HIGH. `REAL_VALUE_FROM_INT (X, LOW, HIGH)' Макрокоманда, для выражения на С, которое преобразовывает целое число двойной точности, расположенное в двух переменных типа INT - LOW и HIGH, в вещественное число, которое затем записывается в Х. 6.19. Смешанные параметры Здесь представлено несколько смешанных параметров. `PREDICATE_CODES' Определите его, если Вы предварительно определили специальные предикаты в файле `MACHINE.c'. Эта макрокоманда вызывается в инициализаторе массива структур. Первое поле в структуре - имя предиката, второе - массив кодов RTL. Для каждого предиката существует список всех кодов RTL, которые могут встречаться в выражениях, соответствующих предикату. Элементы этого списка должны разделяться запятыми. Вот пример двух списков типичных для RISC машины: #define PREDICATE_CODES \ {"gen_reg_rtx_operand", {SUBREG, REG}}, \ {"reg_or_short_cint_operand", {SUBREG, REG, CONST_INT}}, Определение этой макрокоманды не повлияет на сгенерированный код (тем не менее, некорректные определения, в которых пропущен ЫЫЫ код, которому может быть поставлен в соответствие предикат, могут привести к ошибке транслятора). Вместо этого оно сделает таблицу, построенную с помощью `genrecog', более компактной и эффективной. И это ускорит транслятор. Важнее всего включить в список, определяемый этой макрокомандой, предикаты, используемые в большинстве insn. `CASE_VECTOR_MODE' Псевдоним для имени типа. Это тип, который должны иметь элементы таблицы переходов. `CASE_VECTOR_PC_RELATIVE' Определите эту макрокоманду, если таблицы переходов содержат относительные адреса. `CASE_DROPS_THROUGH' Определите эту макрокоманду, если выход значения указателя за разрешенные пределы приводит к потере управления над 'case' insn. Это означает, что определенная по умолчанию метка в действительности игнорируется `CASE_VALUES_THRESHOLD' Определите эту макрокоманду, как наименьшее число, при котором лучше использовать таблицу переходов, чем дерево условных ветвей. По умолчанию она равна четырем для машин с `casesi' инструкциями, и пять для всех остальных. Это самое лучшее для большинства машин значение. `WORD_REGISTER_OPERATIONS' Определите эту макрокоманду, чтобы операции между регистрами с интегральным режимом (типом) размера меньше, чем размер слова, всегда выполнялись на всем регистре. Большинство RISC машин имеют такую возможность, а большинство CISC машин нет. `LOAD_EXTEND_OP (MODE)' Определите эту макрокоманду, как выражение на C, указывающее, когда insns, читающие из памяти тип MODE - интегральный тип уже чем слово, делают либо знаковое расширение, либо расширение нулем, устанавливая биты вне MODE. Для тех типов MODE, которые надо расширять нулем макрокоманда должна возвратить `ZERO_EXTEND', для типов знакового расширения - `SIGN_EXTEND', и `NIL' для остальных. Эта макрокоманда не вызывается если MODE не интегральный тип, или если размер MODE больше или равен, чем `BITS_PER_WORD', поэтому в таких случаях Ваша макрокоманда может возвращать что угодно. Не определяйте эту макрокоманду если она всегда будет возвращать 'NIL'. На машинах, на которых эта макрокоманда определена, Вы обычно будете определять ее как константу `SIGN_EXTEND', либо как константу `ZERO_EXTEND'. `IMPLICIT_FIX_EXPR' Псевдоним для tree кода, который следует использовать по умолчанию для преобразования вещественных значений в значения с фиксированной точкой. Обычно используется `FIX_ROUND_EXPR'. `FIXUNS_TRUNC_LIKE_FIX_TRUNC' Определите эту макрокоманду, если те же самые команды, которые преобразовывают вещественное число в число с плавающей точкой, так же правильно преобразовывают в беззнаковое. `EASY_DIV_EXPR' Псевдоним для tree кода, который в общем случае является наиболее простым видом деления для компиляции кода. Он может быть `TRUNC_DIV_EXPR', `FLOOR_DIV_EXPR', `CEIL_DIV_EXPR' или `ROUND_DIV_EXPR'. Эти четыре оператора деления различаются тем, как они округляют результаты до целого. `EASY_DIV_EXPR' используется, когда разрешимо использовать любой из этих операторов и выбор должен быть сделан исходя из их сравнительной эффективности. `MOVE_MAX' Максимальное число байтов, которые одна команда может быстро переместить из памяти в память. `MAX_MOVE_MAX' Максимальное число байтов, которые одна команда может быстро переместить из памяти в память. Если она не определена, то по умолчанию берется `MOVE_MAX'. Иначе, это - постоянное значение, которое является самым большим из значений, которые принимает 'MOVE_MAX'. `SHIFT_COUNT_TRUNCATED' Выражение на C, которое не равно нулю, если на этой машине число битов, фактически используемых для счета операции сдвига, равно числу битов, которое требуется для представления перемещаемого объекта. Когда эта макрокоманда не ноль, транслятор исходит из предположения, что безопаснее опустить знако-расширяющие, нуль-расширяющие и некоторые выполняющие побитовое "и" инструкции, которые усекают размер сдвига. На машинах, имеющих инструкции, которые действуют на битовые поля в различных позициях, которые могут включать в себя инструкции `bit test', ненулевой `SHIFT_COUNT_TRUNCATED' также осуществляет уничтожение усеканий значений, которые служат аргументами для инструкций битовых полей. Если оба типа инструкций усекают размер (сдвигов) и позицию (для операций битовых полей), или если не существует инструкций битовых полей с переменной позицией, Вам следует определить эту макрокоманду. Тем не менее, на некоторых машинах, таких как 80386 и 680x0, усечение работает только с операциями сдвига и не работает с (реальными или симулируемыми) операциями битовых полей. Определите для таких машин значение `SHIFT_COUNT_TRUNCATED' равным нулю. Вместо этого добавьте образцы в файл `md', который включает подразумеваемые усечения инструкций сдвигов. Вы можете не определять эту макрокоманду если она всегда будет иметь значение ноль. `TRULY_NOOP_TRUNCATION (OUTPREC, INPREC)' Выражение на С, которое не является нулем, если на этой машине безопасно "преобразовывать" целое число размером INPREC бит в целое размером OUTPREC (где OUTPREC меньше чем INPREC) простым обращением с ним, как если бы оно имело только OUTPREC биты. На многих машинах, это выражение может быть 1. Когда `TRULY_NOOP_TRUNCATION' возвращает 1 для пары размеров типов, для которых `MODES_TIEABLE_P' равен 0, может быть сгенерирован неоптимальный код. В таком случае назначение `TRULY_NOOP_TRUNCATION' нулём для таких ситуаций может улучшить положение. `STORE_FLAG_VALUE' Выражение на C, которое описывает значение, возвращенное оператором сравнения с интегральным типом и сохраненное командой сохранения флага (`sCOND') когда условие истинно. Это описание должно прилагаться ко *всем* `sCOND' образцам, и ко всем операторам сравнения, чьи результаты имеют тип `MODE_INT'. Значение 1 или -1 означает, что инструкция, содержащая сравнение, возвращает соответственно 1 или -1, когда сравнение - истина и 0, когда сравнение - ложь. Другое значение указывает какие биты результата гарантированно равны 1, когда сравнение - истина. Это значение интерпретируется в типе операции сравнения, который определяется типом первого операнда в `sCOND' образце. Будет установлен либо нижний, либо знаковый бит `STORE_FLAG_VALUE'. Транслятором используются только эти биты. Если `STORE_FLAG_VALUE' ни 1 ни -1, то транслятор сгенерирует код, зависящий только от определенного бита. Он так же может заменить операторы сравнения на эквивалентные операции, даже если они действуют только на эти особенные биты и оставляют другие биты неопределенными. Например, на машинах, чьи операторы сравнения возвращают значение типа `SImode' и где `STORE_FLAG_VALUE' определен как `0x80000000', т.е. только знаковый бит имеет значение, выражение: (ne:SI (and:SI X (const_int POWER-OF-2)) (const_int 0)) может быть преобразовано к (ashift:SI X (const_int N)) где N подходящий размер операции сдвига для перемещения проверяемого бита в знаковый бит. Не существует способа описать машину, которая всегда устанавливает бит младшего разряда для значения истина, но не гарантирует значения никаких других битов, но мы не знаем таких машин. Если Вы пытаетесь установить GNU CC на такой машине, включите инструкцию, выполняющую логическое "и" с 1 в образце для операторов сравнения и дайте нам знать (см. Как вывести сообщение об ошибке). Часто машина будет иметь много инструкций, которые получают значения от сравнений (или кодов условия). Вот правила для выбора значения для `STORE_FLAG_VALUE', и следовательно инструкций, которые нужно использовать: ╥ Используйте самую короткую последовательность, которая дает определение для `STORE_FLAG_VALUE'. Для транслятора более эффективно "нормализовать" значение самому (преобразовать его, к примеру, в 1 или 0), чем это будут делать сравнения, т.к. у транслятора может быть возможность соединить нормализацию с другими операциями. ╥ Для последовательностей одинаковой длины используйте значение 1 или -1, причем на машинах с долгими переходами немного предпочтительнее -1, а на других машинах предпочтительнее 1. ╥ Вы можете так же выбрать значение `0x80000001' если существуют инструкции, устанавливающие бит знака и младший бит, но не определяющие значения остальных. ╥ В другом случае используйте значение `0x80000000'. Многие машины могут генерировать значение, выбранное для `STORE_FLAG_VALUE', и его отрицание в таком же числе инструкций. На таких машинах Вам следует так же определить образец для этих случаев, например, образец, соответствующий (set A (neg:M (ne:M B C))) Некоторые машины могут так же выполнять операции `and' или `plus' над значениями кода условия и с меньшим количеством инструкций, чем соответствующий insn `sCOND', сопровождаемый `and' или `plus'. На этих машинах Вам следует определить нужные образцы. Используйте имена `incscc' и `decscc' соответственно, для образцов, которые выполняют операции `plus' или `minus' над значениями кода условия. Для примеров смотрите `rs6000.md'. Вы можете использовать GNU Superoptizer, что бы найти такие последовательности инструкций на других машинах. Вам не нужно определять `STORE_FLAG_VALUE', если машина не имеет инструкций сохранения флага. `FLOAT_STORE_FLAG_VALUE' Выражение на C, которое дает ненулевое вещественное значение, возвращаемое, когда операция сравнения с вещественным результатом истина. Определите эту макрокоманду на машине, если она имеет операции сравнения, возвращающие значение с плавающей точкой. Если же таких команд нет, то определять эту макрокоманду не надо. `Pmode' Псевдоним для машинного режима указателей. На большинстве машин, эта макрокоманда определяется как целочисленным режимом, соответствующий ширине аппаратного указателя; `SImode' на 32-битных машинах и `DImode' на 64-битных. На некоторых машинах эта макрокоманда должна быть одним из частичных целых типов, таких как `PSImode'. Ширина `Pmode' должна быть по крайней мере такого же размера, как значение `POINTER_SIZE'. Если она не равна, то Вы должны определить макрокоманду `POINTERS_EXTEND_UNSIGNED' чтобы определить, как расширить указатель до `Pmode'. `FUNCTION_MODE' Псевдоним для типа, используемого для ссылок на функции, вызываемые в RTL выражениях вызова. На большинстве машин этот тип должен быть `QImode'. `INTEGRATE_THRESHOLD (DECL)' Выражение на С для максимального числа инструкций, для которых функцию DECL можно сделать inline. DECL есть узел `FUNCTION_DECL'. Значение этой макрокоманды по умолчанию равно 64 плюс число аргументов, которое понимает функция, умноженное на 8. Некоторые люди полагают, что на RISC машинах следует использовать большее пороговое значение. `SCCS_DIRECTIVE' Определите ее, если препроцессору следует игнорировать директивы `#sccs', и не выдавать сообщения об ошибках. `NO_IMPLICIT_EXTERN_C' Определите эту макрокоманду, если системные файлы описания поддерживают С++ так же, как и С. Эта макрокоманда отключает обычный метод использования системных файлов описания, симулирующий, что содержимое файлов заключено в `extern "C" {...}'. `HANDLE_PRAGMA (STREAM)' Определите эту макрокоманду, если Вы хотите вставлять какие-нибудь прагмы. Если она определена, то выражение, выполняющееся, когда замечена `#pragma', должно быть выражением на С. Аргумент STREAM есть входной поток stdio из которого читается текст источника. Идея использования новых `#pragma' как правило плохая идея. Единственная причина определить эту макрокоманду - для совместимости с другими трансляторами, поддерживающими `#pragma' ради уже существующих программ юзера, которые ее используют. `VALID_MACHINE_DECL_ATTRIBUTE (DECL, ATTRIBUTES, IDENTIFIER, ARGS)' Если определен, то это выражение на С, чье значение не ноль, если IDENTIFIER с аргументами ARGS есть специфичный корректный машинный атрибут для DECL. Атрибуты в ATTRIBUTES были предварительно назначены для DECL. `VALID_MACHINE_TYPE_ATTRIBUTE (TYPE, ATTRIBUTES, IDENTIFIER, ARGS)' Если определен, то это выражение на С, чье значение не ноль, если IDENTIFIER с аргументами ARGS есть специфичный корректный машинный атрибут для TYPE. Атрибуты в ATTRIBUTES были предварительно назначены для TYPE. `COMP_TYPE_ATTRIBUTES (TYPE1, TYPE2)' Если определен, то это выражение на С, чье значение ноль, если атрибуты на TYPE1 и TYPE2 несовместимы, один если совместимы, и два если они почти совместимы (в этом случае выдастся warning). `SET_DEFAULT_TYPE_ATTRIBUTES (TYPE)' Если определена, утверждение C, которое назначает заданные по умолчанию атрибуты к недавно определенному ТИПУ. `DOLLARS_IN_IDENTIFIERS' Определите эту макрокоманду, что бы контролировать использование символа '$' в именах идентификатора. Значение должно быть 0, 1 или 2. 0 значит, что по умолчанию '$' не позволен; 1 значит, что позволен, если используется `-traditional'; 2 значит, что позволен, если `-ansi' не используется. По умолчанию 1. В этом случае нет надобности определят эту макрокоманду. `NO_DOLLAR_IN_LABEL' Определите эту макрокоманду, если ассемблер не принимает меткам с именами, содержащими символ '$'. По умолчанию в именах конструкторов и деструкторов в G ++ содержится символ '$'. Если эта макрокоманда определена, то вместо '$' используется `.'. `NO_DOT_IN_LABEL' Определите эту макрокоманду, если ассемблер не принимает меткам с именами, содержащими символ '.'. По умолчанию в именах конструкторов и деструкторов в G ++ содержится символ '.'. Если эта макрокоманда определена, то вместо имена переделываются, что бы избежать этого символа. `DEFAULT_MAIN_RETURN' Определите эту макрокоманду, если целевая система считает, что функция `main' каждой программы по умолчанию возвратит стандартное значение "успеха" (если никакое другое значение явно не возвращено). Определение должно быть оператором на С (с ';'), генерирующем соответствующие rtl команды. Оно используется только при компилировании конца `main'. `HAVE_ATEXIT' Определите ее, если целевая система поддерживает функцию `atexit' из ANSI C стандарт. Если она не определена, и не определен `INIT_SECTION_ASM_OP', то `exit' функция, заданная по умолчанию, будет поддерживать C ++. `EXIT_BODY' Определите ее, если ваша `exit' функция должна делать что-нибудь кроме вызова внешней функции `_cleanup' перед завершением с `_exit'. Макрокоманда `EXIT_BODY' необходим только, если не определены ни `INSN_SETS_ARE_DELAYED (INSN)' Определите эту макрокоманду как выражение на C, которое отлично от нуля, если планировщик слота задержки может безопасно, поместить команды в слот задержки INSN, даже если их появление устанавливает или уничтожпет ресурсы в INSN. INSN - всегда или 'jump_insn' или 'insn'; GNU CC знает, что каждый 'call_insn' ведет себя так. На машинах, на которых некоторые 'insn' или 'jump_insn' действительно являются вызовами функции и следовательно имеют такое поведение, Вы должны определить эту макрокоманду. Вам не надо определять эту макрокоманду если она всегда будет возвращать ноль. `INSN_REFERENCES_ARE_DELAYED (INSN)' Определите эту макрокоманду как выражение на C, которое отлично от нуля, если планировщик слота задержки может безопасно, поместить команды в слот задержки INSN, даже если их появление устанавливает или уничтожает ресурсы в INSN. INSN - всегда или 'jump_insn' или 'insn'. На некоторых машинах, на которых `insn' или `jump_insn' есть настояшие вызовы функции и их операнды - регистры, которые фактический используют в вызвающей подпрограмме, Вы должны определить эту макрокоманду. Такое выполнение позволяет планировщику слота задержки перемещать команды, копирующие параметры в регистры аргументов в слоте задержки INSN. Вы были не должны определять эту макрокоманду, если она всегда возвращает ноль. `MACHINE_DEPENDENT_REORG (INSN)' В редких случаях(ящиках), корректная генерация объектного кода требует дополнительной машинно-зависимо обработки между вторым проходом оптимизации переходов и отсроченным планированием перехода. На таких машинах, определите эту макрокоманду как оператор на C, чтобы действовать на код, начинающийся в INSN. 7. Файл конфигурации Файл конфигурации `xm-MACHINE.h' содержит макроопределения, описывающие машину и систему, на которых производится трансляция, в отличии от определений в `MACHINE.h', которые описывают целевую машину. Большинство значений в `xm-MACHINE.h' в действительности одинаковы для всех машин, на которых работает GNU CC, поэтому все файлы конфигурации имеют большие одинаковые части. Но существуют макрокоманды, которые отличаются: `USG' Определите эту макрокоманду, если главная система - System V. `VMS' Определите эту макрокоманду, если главная система - VMS. `FATAL_EXIT_CODE' Выражение на C для кода состояния, который будет возвращен, при выходе транслятора после серьезных ошибок. `SUCCESS_EXIT_CODE' Выражение на C для кода состояния, который будет возвращен, когда транслятор выходит без серьезных ошибок. `HOST_WORDS_BIG_ENDIAN' Определите эту макрокоманду, если на главной машине слова в составном слове идут в обратном порядке. (GNU CC не зависит от упорядочения байтов внутри слова на главной машине.) `HOST_FLOAT_WORDS_BIG_ENDIAN' Определите эту макрокоманду, которая должна быть 1 если на главной машине при сохранение вещественных чисел типов `DFmode', `XFmode' или `TFmode' бит знака записывается в самый маленький адрес. В противном случае она должна быть ноль. Если упорядочивание такое же как и упорядочивание целых чисел в составном слове (тип из нескольких слов). `HOST_FLOAT_FORMAT' Числовой код, отличающий вещественной формат главной машины. См. `TARGET_FLOAT_FORMAT' в для различных возможных вариантов и значения по умолчанию. `HOST_BITS_PER_CHAR' Выражение на C для числа битов в `char' на главной машине. `HOST_BITS_PER_SHORT' Выражение на C для числа битов в `short' на главной машине. . `HOST_BITS_PER_INT' Выражение на C для числа битов в `int' на главной машине. `HOST_BITS_PER_LONG' Выражение на C для числа битов в `long' на главной машине. `ONLY_INT_FIELDS' Определите эту макрокоманду, чтобы указать, что главный транслятор поддерживает битовые поля только для 'int', но не для других целых типов, включая `enum', как то делают большинство компиляторов С. `OBSTACK_CHUNK_SIZE' Выражение на C для размера обычных obstack кусков. Если Вы не определяете, его по умолчанию используется разумное обычное значение. `OBSTACK_CHUNK_ALLOC' Функция, используемая, чтобы распределить obstack куски. Если Вы не ее, то будет использоваться `xmalloc'. `OBSTACK_CHUNK_FREE' Функция, используемая, чтобы освободить obstack куски. Если Вы не ее, то будет использоваться `free'. `USE_C_ALLOCA' Определите эту макрокоманду, чтобы указать, что транслятор работает с 'alloca', выполненной на C. Эта версия 'alloca' может быть найдена в файле 'alloca.c'; чтобы использовать ее, Вы должны также измениться переменную 'Makefile'-а - 'ALLOCA'. (Для систем, для которых мы знаем, что это необходимо, это выполнено автоматически) Если Вы определяете эту макрокоманду, Вы должны вероятно сделать это следующим образом: #ifndef __GNUC__ #define USE_C_ALLOCA #else #define alloca __builtin_alloca #endif так, чтобы, когда транслятор компилировался с GNU CC, он использовал более эффективную встроенную функцию `alloca'. `FUNCTION_CONVERSION_BUG' Определите эту макрокоманду, чтобы указать, что главный транслятор не правильно обрабатывает преобразование функционального значения к указатель-в-функцию, когда это используется в выражении. `HAVE_VPRINTF' Определите ее, если библиотечная функция `vprintf' является доступной на вашей системе. `MULTIBYTE_CHARS' Определите эту макрокоманду, чтобы допустить поддержку для символов мультибайта во вводе GNU CC. Это требует, когда главная система поддерживала ANSI C библиотечными функциями для преобразования символов мультибайта к широким символам. `HAVE_PUTENV' Определите ее, если библиотечная функция `putenv' является доступной на вашей системе. `POSIX' Определите ее, если ваша система - POSIX.1 совместима. `NO_SYS_SIGLIST' Определите ее, если ваша система *не* поддерживает переменную `sys_siglist'. `DONT_DECLARE_SYS_SIGLIST' Определите ее, если ваша система имеет переменную `sys_siglist', и она уже объявлена в системных файлах заголовков. `USE_PROTOTYPES' Определите эту макрокоманду 1-ой, если Вы знаете, что главный транслятор поддерживает прототипы, даже если это не определяет __STDC__, или определите ее 0, если Вы не хотите, что бы в GNU CC использовались какие то ни было прототипы. Если `USE_PROTOTYPES' не определена, то это буде определяться автоматический с помощью проверки определено ли `__STDC__'. `NO_MD_PROTOTYPES' Определите ее, если Вы же, что бы было подавление прототипов, сгенерированных их файла описания машины, но использовать другие прототипы внутри GNU CC. Если 'USE_PROTOTYPES' определен 0-ем, или главный транслятор не поддерживает прототипы, эта макрокоманда не будет иметь никакого эффекта. `MD_CALL_PROTOTYPES' Определите ее, если Вы желаете генерировать прототипы для функции машины. Если 'USE_PROTOTYPES' определен 0-ем, или главный транслятор не поддерживает прототипы, или определен 'NO_MD_PROTOTYPES', то эта макрокоманда не воспринимается. Как только все машинные описания изменяются, чтобы иметь соответствующее число параметров, эта макрокоманда будет удалена. В некоторые системах есть соответствующая переменная, но она может иметь разные имена, типа '_sys_siglist'. На этих системах, Вы можете определять 'sys_siglist' как макрокоманде, которая расширяется в фактическое переменной. `NO_STAB_H' Определите ее, если ваша система не имеет инклюд файла 'stab.h'. Если 'USG' определен, то 'NO_STAB_H' определен. `PATH_SEPARATOR' Эта макрокоманда должна быть определена символом, который используется для разделения в путях. По умолчанию используется символ двоеточия. `DIR_SEPARATOR' Если Ваша система использует символ, отличный от слэша для разделения имен директорий в указании файла, то определите его в эту макрокоманду. Когда GNU CC отображает имя файла будет использоваться определенный символ. GNU CC проверит и слыш и вами определенный символ при разборе имени файла. `OBJECT_SUFFIX' Определите эту макрокоманду, как расширение объектных файлов на вашей машине. По умолчанию используется `.o'. `EXECUTABLE_SUFFIX' Определите эту макрокоманду, как расширение исполняемых файлов на вашей машине. По умолчанию используется пустая строка. `COLLECT_EXPORT_LIST' Если определено, `collect2' просмотрит индивидуальные объектные файлы, определенные в командной строке и создаст экспортный список для линкера. Определите эту макрокоманду для систем подобных AIX, где линкер отбрасывает объектные файлы, не вызванные из 'main' или пользовательского export-списка. Кроме того, файлы конфигурации для системы V определяют `bcopy', `bzero' и `bcmp' как псевдонимы. Некоторые файлы определяют 'alloca' как макрокоманду при компиляции GNU CC, чтобы воспользоваться преимуществом встроенного в GNU CC 'alloca'. 8. Фрагменты Makefile-а Когда Вы конфигурируете GNU CC используя сценарий `configure' (см. Инсталяция) из файла шаблона `Makefile.in' создастся файл `Makefile'. При этом включается файлы фрагментов '-TARGET' и `x-HOST' из директории `config'. Если эти файлы не существуют, то это означает, что к главной и целевой машине ничего не надо добавлять. 8.1. Фрагменты Makefile, отвечающие целевой машине Фрагменты Makefil `t-TARGET', отвечающие целевой машине определяют специальные переменные, зависящие от этой машины: `LIBGCC1' Правило, использующие, чтобы сформировать 'libgcc1.a'. Если Ваша целевая машина не должна использовать функции из 'libgcc1.a', установите его пустым. см. Интерфэйс. `CROSS_LIBGCC1' Правило, использующие, чтобы сформировать 'libgcc1.a при формировании кросс-компилятора. Если Ваша целевая машина не должна использовать функции из 'libgcc1.a', установите его пустым. `LIBGCC2_CFLAGS' Трансляторный флаг для использование во время компилирования `libgcc2.c'. `LIB2FUNCS_EXTRA' Список исходных файлов, которые должны быть скомпилированы или скомпилированы ассемблером, а потом вставлены в into `libgcc.a'. `CRTSTUFF_T_CFLAGS' Специальный флаг, использующийся во время компилирования `crtstuff.c'. см. Инициализация. `MULTILIB_OPTIONS' Для некоторых целевых машин, вызывая GNU CC различными способами будут произведены объекты, которые не могут быть слинкованы вместе. Например, для некоторых целевых машин GNU CC производит оба, и большой и маленький конечный код. Для таких целевых машин, Вы должны принять меры для многократного компилирования 'libgcc.a', по одному разу для каждого набора несовместимых опций. Когда GNU CC вызывает компоновщик, то он предлагает правильные версии `libgcc.a', основываясь на используемых опциях командной строки. Макрокоманда 'MULTILIB_OPTIONS' вносит в список набор опций, для которых должны быть сформированы специальные версии 'libgcc.a'. Опции, которые являются взаимно несовместимыми, пишите рядом, через слэш. Опции, которые могут использоваться вместе, пишите через пробелом. Формирующаяся процедура сформирует все комбинации совместимых опций. Например, если Вы устанавливаете 'MULTILIB_OPTIONS' на `m68000/m68020 msoft-float', 'Makefile' будет формировать специальные версии из ' libgcc.a ' используя опций '-m68000', '-m68020', `MULTILIB_DIRNAMES' Если используется 'MULTILIB_OPTIONS', то эта переменная определяет имена директории, которые должны использоваться, для содержания различных библиотек. Пишите один элемент в 'MULTILIB_DIRNAMES' для каждого элемента в 'MULTILIB_OPTIONS'. Если 'MULTILIB_DIRNAMES' не используется, значение по умолчанию будет 'MULTILIB_OPTIONS', со всеми слешами, обрабатываемыми как пробелы. Например, если 'MULTILIB_OPTIONS' - `m68000/m68020 msoft-float', то значение по умолчанию 'MULTILIB_DIRNAMES' будет `m68000 m68020 msoft-float'. Вы можете определить другое значение, если Вы желаете получить другой набора имен каталогов. `MULTILIB_MATCHES' Иногда та же самая опция может быть написана двумя различными способами. Если опция перечислена в 'MULTILIB_OPTIONS', GNU CC знать асе ее синонимы. В этом случае, установите 'MULTILIB_MATCHES' в списке элементов формы 'option=option', чтобы описать все уместные синонимы. Например, 8.2. Фрагменты Makefile, отвечающие главной машине Фрагмент makefile главной машины, `x-HOST', определяет специальные, зависимые от главной машины переменные и цели, используемые в `Makefile': `CC' Транслятор выполняет, когда строит первую страницу. `CLIB' Что бы прилинковываться к библиотеками главной машины. `OLDCC' Для использования транслятором, при формировании `libgcc1.a' для местной трансляции. `OLDAR' Версия `ar' для использования, когда строится `libgcc1.a' для местной трансляции. `INSTALL' Программа инсталяции.