Большинство работы транслятора выполняется в промежуточном представлении, называемом register transfer language (RTL) .На этом языке команды, которые нужно выводить, описаны довольно подробно одна за другой в алгебраической форме, которая описывает то, что делает команда.
RTL построен на идеях списков Lisp. Он имеет как внутреннюю форму, состоящую из структур, которые указывают на другие структуры, так и текстовую форму, которая используется в машинном описании и в напечатанных дампах отладки. Текстовая форма использует вложенные круглые скобки, чтобы определить указатели во внутренней форме.
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) ".
Для каждого типа выражения " rtl.def " указывает количество содержащихся объектов и их виды следующим способом: " e " для выражения (фактически указателя на выражение), " i " для целого числа, " w " для широкого целого числа, " s " для строки и " E " для вектора выражений. Последовательность символов для кода выражения называется его форматом. Таким образом, форматом " subreg " является " ei ".
Иногда используются несколько других символов управления форматом:
- " u "
" u " эквивалентно " e " за исключением того, что оно печатается по-другому в отладочных дампах. Это используется для указателей на insns.
- " n "
" n " эквивалентно " i " за исключением того, что оно печатается по-другому в отладочных дампах. Это используется для номера строки или количества кода insn " note ".
- " S "
" S " обозначает строку, которая является необязательной. В объектах RTL в ядре " S " эквивалентно " s ", но когда объект читается из " md " файла, строковое значение этого операнда может быть опущено. Опущенная строка считается пустой.
- " V "
" V " обозначает вектор, который является необязательным. В объектах RTL в ядре " V " эквивалентно " E ", но когда объект читается из " md " файла, векторное значение этого операнда может быть опущено. Опущенный вектор - то же, что и вектор, не имеющий элементов.
- " 0 "
" 0 " означает слот, чей содержание не попадает ни в какую нормальную категорию. " 0 " слоты не печатаются вообще в дампах, и часто используются специальными способами в маленьких частях транслятора.
Имеются макрокоманды для получения числа операндов, формата и класса кода выражения:
- " GET_RTX_LENGTH (CODE) "
Число операндов RTX кода CODE.
- " GET_RTX_FORMAT (CODE) "
Формат RTX кода CODE как строка C.
- " GET_RTX_CLASS (CODE) "
Одиночный символ, представляющий тип операции RTX, которую выполняет код CODE.
Определены следующие классы:
- " o "
RTX код, который представляет фактический объект, типа " reg " или " mem ". " subreg " не принадлежит к этому классу.
- " < "
RTX код для сравнения. Коды в этом классе " NE ", " EQ ", " LE ", " LT ", " GE ", " GT ", " LEU ", " LTU ",
" GEU ", " GTU ".
- " 1 "
RTX код для унарной арифметической операции, типа " neg ".
- " c "
RTX код для коммутативной бинарной операции, отличной от " NE " и " EQ " (которые имеют класс " < ").
- " 2 "
RTX код для некоммутативной двоичной операции, типа " MINUS ".
- " b "
RTX код для операции с битовым полем, " ZERO_EXTRACT " или " SIGN_EXTRACT ".
- " 3 "
RTX код для трех других входных операций, типа " IF_THEN_ELSE ".
- " i "
RTX код для машинного insn (" INSN ", " JUMP_INSN " и " CALL_INSN ").
- " m "
RTX код для того,что соответствует insns, типа " MATCH_DUP ".
- " x "
Все другие RTX коды.
К операндам выражений обращаются, используя макрокоманды " XEXP ", " XINT ", " XWINT " и " XSTR ". Каждая из этих макрокоманд имеет два параметра: указатель на выражение (RTX) и номер операнда (отсчитываемый от нуля). Таким образом,
обращается к операнду 2 выражения X как к выражению.
обращается к тому же самому операнду как к целому числу. Макрокоманда" 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 ", чтобы обратиться к элементам и длине вектора.
- " XVEC (EXP, IDX) "
Получает указатель на вектор, который является операндом номер IDX в выражении EXP.
- " XVECLEN (EXP, IDX) "
Получает длину (число элементов) в векторе, который является операндом номер IDX в выражении EXP. Это значение типа " int ".
- " XVECEXP (EXP, IDX, ELTNUM) "
Обращается к элементу номер ELTNUM в векторе, который является операндом номер IDX в выражении EXP. Это значение RTX.
При вызове этой макрокоманды нужно быть уверенным, что ELTNUM не меньше нуля и меньше чем " XVECLEN (EXP, IDX) ".
Все макрокоманды, определенные в этом разделе, расширяются в именующие выражения и, следовательно, могут использоваться для присваивания значения операндам, длинам и элементам векторов так же, как и для обращения к ним.
Выражения RTL содержат несколько флагов (битовых полей с одним битом), которые используются в некоторых типах выражений. Наиболее часто к ним обращаются следующими макрокомандами:
- " MEM_VOLATILE_P (X) "
В выражениях " mem " отлично от нуля для переменных ссылок памяти. Сохраняется в поле " volatil " и печатается как " /v ".
- " MEM_IN_STRUCT_P (X) "
В выражениях " mem " отлично от нуля для ссылки на всю структуру, объединение или массив, или на их компонент. Ноль для ссылки на скалярную переменную или через указатель на скаляр. Сохраняется в поле " in_struct " и печатается как " /s ".
- " REG_LOOP_TEST_P "
В выражениях " reg " отлично от нуля, если этот регистр "живет" только в коде проверки на выход некоторого цикла. Сохраняется в поле " in_struct " и печатается как " /s ".
- " REG_USERVAR_P (X) "
В " reg " отлично от нуля, если соответствует переменной, имеющейся в исходном тексте пользователя. Ноль для временных переменных, внутренне сгенерированных транслятором. Сохраняется в поле " volatil " и печатается как " /v ".
- " REG_FUNCTION_VALUE_P (X) "
Отлично от нуля в " reg ", если это - место, в которое должно быть возвращено значение функции. (Это возможно только для аппаратного регистра.) Сохраняется в поле " integrated " и печатается как " /i ".
Тот же самый аппаратный регистр может использоваться также для значений функций, вызываемых этой, но " REG_FUNCTION_VALUE_P " - ноль для этого вида использования.
- " SUBREG_PROMOTED_VAR_P "
Отлично от нуля в " subreg ", если делается во время доступа к объекту, продвинутому на более широкий тип в соответствии с макрокомандой машинного описания " PROMOTED_MODE " (* См.: Размещение памяти::.). В этом случае тип " subreg " является объявленным типом объекта и тип " SUBREG_REG " является типом регистра, который содержит объект. Расширяемые ые переменные всегда расширяются нулем или знаком в более широкий тип при каждом присваивании. Сохраняется в поле " in_struct " поле и печатается как " /s ".
- " SUBREG_PROMOTED_UNSIGNED_P "
Отлично от нуля в " subreg " который имеет " SUBREG_PROMOTED_VAR_P "
отличным от нуля, если вызываемый объект расширен нулем, и ноль, если он расширен знаком. Сохраняется в поле " unchanging " и печатается как " /u ".
- " RTX_UNCHANGING_P (X) "
Отлично от нуля в " reg " или " mem ", если значение не изменено. (Этот флаг не устанавливается для ссылок на константы через указатели. Такие указатели гарантируют только, что объект будет не изменен явно текущей функцией. Объект может бы быть изменен другими функциями или через совмещение имен.) Сохраняется в поле " unchanging " и печатается как " /u ".
- " RTX_INTEGRATED_P (INSN) "
Отлично от нуля в insn, если получено из вызова встроенной функции. Сохраняется в поле " integrated " и печатается как " /i ". Может быть удалено; в настоящее время от него ничего не зависит.
- " SYMBOL_REF_USED (X) "
В " symbol_ref " указывает, что X использован. Обычно используется только для того, чтобы гарантировать, что X только однажды объявлен внешним. Сохраняется в поле " used ".
- " SYMBOL_REF_FLAG (X) "
В " symbol_ref " используется как флаг для специфических для машины целей. Сохраняется в поле " volatil " и печатается как " /v ".
- " LABEL_OUTSIDE_LOOP_P "
В выражениях ` label_ref ' отлично от нуля, если это - ссылка на метку, которая находится снаружи самого внутреннего цикла, содержащего ссылку на метку. Сохраняется в поле " in_struct " и печатается как " /s ".
- " INSN_DELETED_P (INSN) "
В insn отлично от нуля, если insn был удален. Сохраняется в поле " volatil " и печатается как " /v ".
- " INSN_ANNULLED_BRANCH_P (INSN) "
В " insn " в слоте задержки ветви insn указывает, что должна использоваться аннулирующая ветвь. См. обсуждение " sequence " ниже. Сохраняется в поле " unchanging " и печатается как " /u ".
- " INSN_FROM_TARGET_P (INSN) "
В " insn " в слоте задержки ветви указывает, что insn - из адреса перехода. Если для ветви insn " INSN_ANNULLED_BRANCH_P " установлен, этот insn должен выполняться, только если переход производится. Для аннулированных ветвей с этим битом, равным 0, insn должен выполняться, если переход не производится. Сохраняется в поле " in_struct " и печатается как " /s ".
- " CONSTANT_POOL_ADDRESS_P (X) "
Отлично от нуля в " symbol_ref " если он ссылается на часть текущего "пула констант" функции. Это адреса, близкие к началу функции, и GNU CC предполагает, что они могут быть адресованы непосредственно (возможно, с помощью базовых регистров). Сохраняется в поле " unchanging " и печатается как " /u ".
- " CONST_CALL_P (X) "
В " call_insn " указывает, что insn представляет обращение к постоянной функции. Сохраняется в поле " unchanging " и печатается как " /u ".
- " LABEL_PRESERVE_P (X) "
В " code_label " указывает, что метка никогда не может быть удалена. Метки, вызываемые нелокальным переходом, будут иметь этот набор битов. Сохраняется в поле " in_struct " и печатается как " /s ".
- " SCHED_GROUP_P (INSN) "
В течение планирования команд в insn указывает, что предыдущий insn должен планироваться вместе с этим insn. Это используется, чтобы гарантировать, что определенные группы команд не будут разбиваться на части проходом планирования команд, например, " use " insns перед " call_insn " не могут отделяться от " call_insn ". Сохраняется в поле " in_struct " и печатается как " /s ".
Вот поля, к которым обращаются вышеупомянутые макрокоманды:
- " used "
Обычно этот флаг используется только в один момент, а именно - в конце генерации RTL функции, чтобы посчитать, сколько раз в insns появляется выражение. Выражения, которые появляются больше чем однажды, копируются, согласно правилам совместного использования структур (* См.: Совместное использование::.).
В " symbol_ref " он указывает что внешнее объявление для символа уже было написано.
В " reg " он используется регистром листа, перенумеровывающим код, чтобы гарантировать, что каждый регистр перенумеровывается только однажды.
- " volatil "
Этот флаг используется в выражениях " mem ", " symbol_ref " и " reg " и в insns. В файлах дампа RTL он печатается как " /v ".
В выражении " mem " он равен 1, если ссылка памяти переменная. Переменные ссылки памяти нельзя удалять, переупорядочивать и объединять.
В выражении ` symbol_ref ', это используется для специфических для машины целей.
В выражении ` reg ' он равен 1, если значение - пользовательская переменная, и 0, если это внутренняя временная переменная транслятора.
В insn, значение 1 означает, что insn был удален.
- " in_struct "
В выражениях " mem " он равен 1, если данная величина ссылается на весь массив или структуру или их часть ; 0, если это - скалярная переменная (или может быть ею). Ссылке через указатель C соответствует 0, потому что указатель может указывать на скалярную переменную. Эта информация позволяет транслятору выяснить определенную информацию
относительно возможных случаев совмещения имен.
В insn в слоте задержки перехода 1 означает, что этот insn - из адреса перехода.
Во время планирования команд в insn 1 означает, что этот insn должен планироваться как часть группы вместе с предыдущим
insn.
В выражениях " reg " он равен 1, если регистр "живет" только в коде проверки на выход некоторого цикла.
В выражениях " subreg " 1 означает, что " subreg " обращается к объекту, который имел тип, полученный из более широкого типа.
В выражениях " label_ref " 1 означает, что ссылка на метку находится снаружи самого внутреннего цикла, содержащего insn, в котором было найдено " label_ref "
В выражениях ` code_label ' он равен 1, если метку никогда нельзя удалять. Это используется для меток, на которые имеются
нелокальные переходы.
В дампе RTL этот флаг представляется как " /s ".
- " unchanging "
В выражениях " reg " и " mem " 1 означает, что значение выражения никогда не изменяется.
В выражениях " subreg " он равен 1, если " subreg " ссылается на объект без знака, чей тип был расширен до более широкого тип.
В insn 1 означает, что это - аннулирующая ветвь.
В выражении " symbol_ref ", 1 означает, что этот символ адресует что-то в пуле констант функции.
В " call_insn " 1 означает, что эта команда - обращение к постояннной функции.
В дампе RTL этот флаг представляется как " /u ".
- " integrated "
В некоторых видах выражений, включая insns, этот флаг означает, что RTL был произведен интеграцией процедуры.
В выражении " reg " этот флаг указывает регистр, содержащий значение, которое будет возвращено текущей функцией. На машинах
которые передают параметры в регистрах, то же количество регистров может использоваться и для параметров, но этот флаг не устанавливается при таком использовании.
Тип описывает размер данных объекта и представление его использования. В С-коде машинные рехимы (типы) представляются перечислимым типом `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'
Этот тип комплексных чисел, представляемый парой чисел с плавающей точкой. Значение числа с плавающей точкой будет соответственно типа 'SFmode', `DFmode', `XFmode', или `TFmode'.
`CQImode, CHImode, CSImode, CDImode, CTImode, COImode'
Этот тип комплексных чисел, представляемый парой целых чисел.Значение целого числа будет соответственно типа `QImode', `HImode',`SImode', `DImode', `TImode', или `OImode'.
C-макрос `Pmode' описывает тип, который используется для адреса.Обычно этот тип имеет размер `BITS_PER_WORD' бит. На 32-битовых машинах это `SImode'.
Единственный тип, который обязан поддерживаться машиной - `QImode' и типы соответствующие размерам `BITS_PER_WORD', `FLOAT_TYPE_SIZE' и `DOUBLE_TYPE_SIZE'. По умодчанию, транслятор быдет пытаться использовать для 8-байтных структур тип "DImode", но это может быть предотвращено с помошью отмены области определения `MAX_FIXED_MODE_SIZE'. Аналогично для 16-битных структур транслятор использует `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'.
Простейшее 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'.
Ниже перечислены типы выражений для доступа к машинным регистрам и основной памяти.
- `(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' и значения 'FIRST_PARM_OFFSET'.
`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' используется для обращения к машинному регистру в режеме, отличном от естственного, или что бы обратится к мульти-регистру 'reg', который фактически является несколькими регистрами.
Каждый псевдо регистр имеет свой естественный режим. Если необходимо работать с этим регистром в другом режиме - например, выполнить команду перемещения слова над регистром, который содержит один байт, псевдо регистр должен быть включен в '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 определяет в какком режиме будет выполнено сравнение, поэтому он не моднт быть 'VOIDMode'.Если один из операндов - константа, то она должна быть вторым аргументом и тип сравнения корректируется как соответствующий.Сравнение двух констант некорректно, так как невозможно узнать режим в котором проводить сравнение. Сравнение должно также быть свернуто в течение трансляции, или первый операнд должен быть загружен в регистр,в то время пока режим еще известен.
`(neg:M X)'
Возврашает X с обратным знаком (вычитание из нуля), операция выполняется в режиме M.
`(mult:M X Y)'
Возвращает произведение значений X и Y со знаком, вымолненое в режиме M.Некоторые машимы поддерживают умножение, при котором произведение имеет больший размер, чем операнды. Для этого надо писать умножение в следующем формате:
(mult:M (sign_extend:M X) (sign_extend:M Y))
где M тип, больший, чем типы X и Y, которые не обязаны быть одинаковыми.Для расширяющего умножения без знака аналогично, только исспользуя `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)'
Возвращает один плюс индекс младшего значащего бита в Х, представленное как целое число типа М. (Если Х-ноль, то возвращается ноль.)Тип Х не обязательно должен быть М; в зависимости от целевой машины,могут быть корректными различные комбинации типов.
Операторы сравнения проверяют отношение двух операндов и должны представлять машинно-независимое ненулевое значение, описываемое с помошью `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
Существуют специальные выражения для представления конструкций, т.е. они использующих битовые поля. Эти выражения в 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', но ссылается на беззнаковое или нуль-расширенное битовое поле. Извлекается та же самая последовательность битов, но она заполняет целое слово с нулями вместо знако-расширения.
Все преобразования типов должны быть представлены явными операциями преобразования. Например, выражение, которое является суммой байта и слова не может быть описана следующим образом:`(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.
Выражения объявлений не представляют собой арифметические операции, но устанавливают утверждения относительно операндов:
- `(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.
Коды выражения, описанные до сих пор, представляют значения, а не действия.Но машинные команды никогда не производят значения; они значимы только из-за их побочных эффектов на состоянии машины. Специальные коды выражения используются, чтобы представить побочные эффекты.
Тело команды - всегда один из этих кодов побочного эффекта;
коды, описанные выше, которые представляют значения, появляются только как операнды этих.
- " (set LVAL X) "
Представляет действие сохранения значения X в место,представленное LVAL. LVAL должно быть выражением, представляющим место, в которое можно производить сохранение: " reg " (или " subreg "
или " strict_low_part "), " mem ", " pc " или " cc0 ".
Если LVAL - " reg ", " subreg " или " mem ", оно имеет машинный тип;
тогда X должен подходить для этого типа.
Если LVAL - " reg ", чей машинный тип меньше, чем полная ширина регистра, то это означает, что части регистра,определяемой машинным типом, присваивается указанное значение, а остатку регистра присваивается неопределенное значение. Аналогично, если LVAL - " subreg ", чей машинный тип меньше, чем тип регистра, то остальная часть регистра может измениться произвольным образом.
Если LVAL - " strict_low_part " для " subreg ", то части регистра, определенной машинным типом " subreg ", присваивается значение X, а остальная часть регистра не изменяется.
Если LVAL - " (cc0) ", оно не имеет машинного типа, и X может быть выражением " compare " или значением, которое может иметь любой тип.Последний случай представляет команду " test ". Выражение " (set
(cc0) (reg:M N)) " эквивалентно выражению " (set (cc0) (compare (reg:M N)(const_int 0))) ". Используйте вышеупомянутое выражение, чтобы зарезервировать память во время трансляции.
Если LVAL - " (pc) ", мы имеем команду перехода, и возможности для X весьма ограничены. Это может быть выражение " label_ref "
(безусловный переход). Это может быть " if_then_else "
(условный переход), в этом случае второй или третий операнд должен быть " (pc) " (для случая, когда нет перехода), а другой из них должен быть " label_ref " (для случая, когда есть переход). X может также быть " mem " или " (plus: SI (pc) Y) ", где Y может быть " reg " или " mem "; эти необычные варианты используются для осуществления переходов через таблицы ветвлений.
Если LVAL не является ни " (cc0) ", ни " (pc) ", то тип LVAL должен не быть " VOIDmode " и тип X должен подходить для типа LVAL.
К LVAL обычно обращаются макрокомандой " SET_DEST ", а к X -
макрокомандой " SET_SRC ".
" (return) "
Единственное выражение в образце, представляющее возврат из текущей функции, на машинах, где это может быть выполнено одной командой, типа Vaxes. На машинах, где для возвращения из функции должен быть выполнен многокомандный "эпилог" ,возврат выполняется переходом к метке, которая предшествует эпилогу, и код выражения " return " никогда не используется.
Внутри выражения " if_then_else " представляет значение, которое должно быть помещено в " pc " для возврата в вызывающую функцию.
Обратите внимание, что образец insn " (return) " логически эквивалентен " (set (pc) (return)) ", но последняя форма никогда не используется.
" (call FUNCTION NARGS) "
Представляет обращение к функции. FUNCTION - выражение " mem ", чей адрес - адрес функции, которую нужно вызвать. NARGS -
выражение, которое может использоваться для двух целей: на некоторых машинах оно представляет число байтов параметров в стеке; на других, оно представляет число регистров с параметрами.
Каждая машина имеет стандартный машинный тип, который должна иметь FUNCTION.Машинное описание определяет макрокоманду " FUNCTION_MODE ",преобразующуюся в необходимое имя типа. Цель этого типа -
указать доступные виды адресации на машинах, где доступные виды адресации зависят от машинного типа.
" (clobber X) "
Представляет сохранение или возможное сохранение непредсказуемого,неописанного значения в X, который должен быть выражением " reg "," scratch " или " mem ".
Одно из мест, где это используется - в строковых командах, которые сохраняют стандартные значения в некоторые аппаратные регистры.Описывать сохраняемые значения не очень-то необходимо, но важно сообщить транслятору, что регистры будут изменены, чтобы это он не пытался хранить данные в них во время исполнения строковой команды.
Если X - " (mem: BLK(const_int 0)) ", это означает что всю память следует предполагать изменяемой во время вызова.
Обратите внимание, что машинное описание классифицирует определенные аппаратные регистры как "call-clobbered". Все вызовы функций считаются по умолчанию использующими эти регистры, так что не нужно использовать выражения " clobber ", чтобы указать этот факт. Также предполагается, что каждый вызов функции может изменить любую область памяти, если функция не объявлена " const ".
Если в последней группе выражений в " parallel " каждое является выражением " clobber ", с параметрами-выражениями " reg " или " match_scratch "
(* См.: RTL Шаблон::.), при комбинировании могут добавляться соответствующие выражения " clobber " к создаваемому insn,если это потребуется для согласования.
Эта особенность может использоваться, например, на машине, которая имеет команды сложения и умножения, не использующие регистр MQ, но которая имеет команду добавления, которая задействует MQ регистр. Аналогично,скомбинированная команда могла бы требовать временный регистр,а ее составные части - нет.
Когда выражение " clobber " для регистра появляется внутри a
" parallel " с другими побочными эффектами, распределитель регистров гарантирует, что регистр является незанятым, и до, и после этого insn. Однако, фаза перезагрузки может распределять регистр,используемый для одного из вводов, если для выбранного варианта не указано ограничение " & " (* См.: Модификаторы::.). Вы можете применять " clobber " для аппаратного регистр, псевдорегистра или выражения " scratch "; в последних двух случаях, GNU CC распределит доступный там аппаратный регистр для временного использования.
Для команд, которые требуют временного регистра, Вы должны использовать " scratch " вместо псевдорегистра, потому что это позволит фаза комбинирования добавить " clobber " когда требуется. Это делается кодированием (" clobber " (" match_scratch " ...)). Если Вы используете " clobber " для псевдорегистра, используйте тот, который больше нигде не появляется и генерируйте каждый раз новый. Иначе могут смешаться CSE.
Имеется другое известное использование затирания псевдорегистра в " parallel ": когда один из входных операндов insn также затирается insn. В этом случае, использование одного и того же псевдорегистра для затирания и в другом месте в insn приводит к ожидаемым результатам.
" (use X) "
Представляет использование значения X. Это указывает, что значение X в этой точке программы необходимо, даже если это может быть неочевидно. Следовательно, транслятор не будет пытаться удалять предыдущие команды, которые приводят только к сохранению значения X. X, должен быть выражением " reg ".
В течение фазы планирования отсроченных переходов X может быть insn.Это указывает, что X предварительно был размещен в этом месте кода и зависимость его данных должна быть принята во внимание.Эти " use " insns будут удалено перед окончанием фазы планирования отсроченных переходов.
" (parallel [X0 X1 ...]) "
Представляет несколько побочных эффектов, выполняемых параллельно.Квадратные скобки обозначают вектор; операнд " parallel " является вектором из выражений. X0, X1 и так далее - индивидуальные выражения с побочным эффектом - выражения кода " set ", " call ", " return "," clobber " или " use ".
" In parallel " означает что сначала все значения, используемые в Индивидуальные побочные эффекты вычислены, и секунда весь фактический Побочные эффекты выполняются. Например,
(parallel [(set (reg: SI 1) (mem: SI (reg: SI 1))) (set (mem: SI (reg: SI 1)) (reg: SI 1))])
недвусмысленно говорит, что значения аппаратного регистра 1 и области памяти, адресованной им, обмениваются местами. В обоих местах,где " (reg: SI 1) " появляется как адрес памяти, это относится к значению в регистре 1 *перед* выполнением insn.
Из этого следует, что *неправильно* использовать " parallel " и ожидать,что результат одного " set " будет доступен для следующего. Например,команду "перейти, если ноль" иногда пытаются представлять так:
(parallel [(set (cc0) (reg: SI 34)) (set (PC) (if_then_else
(eq (cc0) (const_int 0)) (label_ref ...) (pc)))])
Но это неправильно, потому что такой код говорит, что условие перехода зависит от значения кода условия *перед* этой командой, а не от нового значения, которое устанавливается этой командой.
Решетчатая оптимизация, которая происходит вместе с заключительным выводом ассемблерного кода, может производить insns, чьи образцы состоят из " parallel ", элементы которых - операнды, необходимые для вывода возникающего в результате ассемблерного кода - часто " reg ", " mem "
или константные выражения. Это не был бы правильно построенный RTL в любой другой стадии трансляции, но в этом случае все нормально, потому что в дальнейшем никакой оптимизации не производится. Однако, определение макрокоманды " NOTICE_UPDATE_CC ", если оно имеется, должно иметь дело с такими insns если Вы определяете какую-либо решетчатую оптимизацию.
" (sequence [INSNS ...]) "
Представляет последовательность insns. Каждое из INSNS, которое появляется в векторе, подходит для появления в цепочке insns, так что оно должно быть " insn ", " jump_insn ", " call_insn ", " code_label "," barrier " или " note ".
" sequence " RTX никогда не помещается в фактический insn во время генерации RTL. Оно представляет последовательность insns, которые получаются " define_expand " *прежде*, чем те insns передаются " emit_insn " для вставки их в цепочку insns. При фактической вставке отдельные sub-insns выделяются и " sequence " исчезает.
После того, как планирование слотов задержки завершено, insn и весь insns, которые находятся в слотах задержки группируются вместе в " sequence ". Insn требует слот задержки первым insn в векторе;
последующий insns должны быть помещены в слот задержки.
" INSN_ANNULLED_BRANCH_P " устанавливается на insn в слоте задержки,чтобы указать, что insn перехода должен использоваться, что будет условно аннулировать эффект insns в слотах задержки. В таком случае," INSN_FROM_TARGET_P " указывает, что insn - из адреса перехода и должен выполняться только, если переход происходит;
иначе insn должен выполняться только, если переход не происходит.* См.: Слоты Задержки::.
Эти коды выражения появляются вместо побочного эффекта, как тело insn, хотя, строго говоря, они не всегда описывают побочные эффекты как таковые:
" (asm_input S) "
Представляет литеральный код ассемблера с помощью строки S.
" (unspec [OPERANDS ...] INDEX) "
" (unspec_volatile [OPERANDS ...] INDEX) "
Представляет машинно-специфическую операцию над OPERANDS. INDEX
задает машинно-специфическую операцию." unspec_volatile " используется для volatile операций и операций,которые могут захватывать; " unspec " используется для остальных операций.
Эти коды могут появляться внутри " pattern " insn, внутри " parallel " или внутри выражения.
" (addr_vec: M [LR0 LR1 ...]) "
Представляет таблицу адресов перехода. Векторные элементы LR0,LR1
и т.д., являются выражениями " label_ref ". Тип M определяет, сколько места выделяется каждому адресу; обычно M должен быть " Pmode ".
" (addr_diff_vec: M BASE [LR0 LR1 ...]) "
Представляет таблицу адресов перехода, выраженных как смещения от BASE. Векторные элементы LR0,LR1 и т.д. являются выражениями " label_ref ",и BASE тоже.Режим M определяет, сколько места выделяется каждому адресу.
Четыре специальных кода выражения побочного эффекта появляются как адреса памяти.
- " (pre_dec: MX) "
Представляет побочный эффект уменьшения X на стандартное число,а также значение, которое X имеет после уменьшения.X должен быть " reg " или " mem ", но большинство машин позволяет только " reg ".М должен быть машинным типом для указателей,используемым на машине.Число, на которое уменьшается X - длина в байтах машинного типа ссылки памяти, для которой это выражение служит адресом. Вот пример использования:
(mem: DF (pre_dec: SI (reg: SI 39)))
Это означает уменьшение псевдорегистра 39 на длину значения " DFmode " и использование результата для адресации значения " DFmode ".
" (pre_inc: M X) "
Аналогично, происходит не уменьшение, а увеличение X.
" (post_dec: M X) "
Представляет тот же самый побочный эффект, что и " pre_dec ", но другое значение. Значение, представляемое здесь - значение X перед уменршением.
" (post_inc: M X) "
Аналогично, происходит не уменьшение, а увеличение X.
Эти вложенные выражения побочного эффекта должны использоваться осторожно.Образцы команд не могут использовать их. До прохода " flow "
транслятора, они могут иметь место только для помещения значений на стек.Проход " flow " находит случаи, когда регистры увеличиваются или уменьшаются в одной команде и используются как адрес непосредственно перед этим или после этого; эти места преобразуются для использования pre- или post- увеличения или уменьшения.
Если регистр, используемый как операнд этих выражений, используется для другого адреса в insn, используется первоначальное значение регистра.Использование регистра вне адреса внутри того же самого insn не разрешается,так же, как и использование в выражении с вложенным побочным эффектом, потому что такие insns ведут себя по-разному на разных машинах и, следовательно, должны обрабатываться неоднозначно и запрещаются.
Команду, которая может быть представлена при помощи вложенного побочного эффекта,можно также представить, используя " parallel ", содержащий дополнительный " set ", чтобы описать, как изменяется регистр адреса. Это не сделано, потому что машины, которые вообще позволяют эти операции, обычно позволяют их всюду, где запрашивается адрес памяти. Описание их как дополнительных сохранений " parallel " потребовало бы удвоения числа входов в машинном описании.
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, ... для последовательных операндов вывода.
RTL представление кода функции - связанная в две стороны цепочка объектов, называемых "insns". Insns - выражения со специальными кодами, которые не используются ни для какой другой цели. Некоторые insns
фактически являются командами; другие представляют таблицы управления для операторов " switch "; другие представляют метки перехода или различных видов описаний.
В дополнение к собственным специфическим данным, каждый insn должен иметь уникальный идентифицирующий номер, который отличает его от всех других insns в текущей функции (после отсроченного планирования перехода копии insn с одинаковыми идентифицирующими номерами могут присутствовать в нескольких местах в функции, но эти копии всегда будут идентичны и будут появляться только внутри " sequence "), и указатели на предыдущий и следующий insn для получения цепочки. Эти три поля занимают одну и ту же позицию в каждом insn, независимо от кода выражения insn. Вообще говоря, к ним можно обращаться при помощи " XEXP "
и " XINT ", но вместо них всегда используется три специальных макрокоманды:
- " INSN_UID (I) "
Обращается к уникальному идентификатору insn I.
" PREV_INSN (I) "
Обращается к указателю цепочки на insn, предшествующий I. Если I -
первый insn, это - нулевой указатель.
" NEXT_INSN (I) "
Обращается к указателю цепочки на insn после I. Если I -
последний insn, это - нулевой указатель.
Первый insn в цепочке получается вызовом " get_insns ";
последний insn - вызовом " get_last_insn ". Внутри цепочки,ограниченной этими insns, " NEXT_INSN " и " PREV_INSN " указатели всегда должны соответствовать друг другу: если INSN - не первый insn,
NEXT_INSN (PREV_INSN (INSN)) == INSN
всегда справедливо, и если INSN - не последний insn,
PREV_INSN (NEXT_INSN (INSN)) == INSN
всегда справедливо.
После планирования слотов задержки, некоторые из insns в цепочке могут быть выражениями " sequence ", которые содержат вектор insns. Значение " NEXT_INSN " для всех, кроме последнего, из этих insns - следующий insn в вектор; значение " NEXT_INSN " для последнего insn в векторе -
такое же, как и значение " NEXT_INSN " для " sequence ", в котором он содержится. Аналогичные правила соблюдаются для " PREV_INSN ".
Это означает, что вышеупомянутые инварианты - не обязательно истина для insns внутри выражений " sequence ". А именно, если INSN -
первый insn в " sequence ", " NEXT_INSN (PREV_INSN (INSN)) " - это insn,содержащий выражение " sequence ", а значение " PREV_INSN( NEXT_INSN (INSN)) "
- это insn, являющийся последним в выражении " sequence ". Вы можете использовать эти выражения, чтобы найти выражение " sequence ", содержащее данный insn.
Каждый insn имеет один из следующих шести кодов выражения:
- " insn "
Код выражения " insn " используется для команд, которые не делают переходов и обращений к функциям. Выражения " sequence "
всегда содержатся в insns с кодом " insn ", даже если один из их insns делает переход или обращение к функции.
Insns с кодом " insn " имеют четыре дополнительных поля после трех обязательных, перечисленных выше. Эти четыре поля описаны ниже в таблице.
- " jump_insn "
Код выражения " jump_insn " используется для команд, которые могут делать переход (или, более общо, могут содержать выражения " label_ref ").Команда возврата из текущей функции регистрируется как " jump_insn ".
" jump_insn " insns имеют те же самые дополнительные поля, что и " insn " insns,обращение к которым производится таким же образом, и, кроме того, содержат поле " JUMP_LABEL ", которое определяется после окончания оптимизации перехода.
Для простого условного и безусловного перехода это поле содержит " code_label ", к которому этот insn будет производить (возможно,условный) переход. В более сложном переходе, в " JUMP_LABEL "
записывается одна из меток, к которым insn обращается; единственый путь для того, чтобы найти остальные - просмотреть все тело insn.
insns возврата рассматриваются как переходы, но так как они не обращаются ни к каким меткам, они имеют ноль в поле " JUMP_LABEL ".
- " call_insn "
Код выражения " call_insn " используется для команд, которые могут делать обращения к функции. Важно отличать эти команды, потому что они подразумевают,что некоторые регистры и области памяти могут непредсказуемо измениться.
" call_insn " insns имеют те же самые дополнительные поля, что и " insn "
insns, обращение к которым производится таким же образом, и, кроме того,содержат поле " CALL_INSN_FUNCTION_USAGE ", который содержит список (цепочка выражений " expr_list "), содержащий " use " и " clobber "
выражения, которые обозначают аппаратные регистры, используемые или затираемые вызываемой функцией. Регистр, указанный в " clobber " в этом списке,изменяется *после* выполнения " call_insn ", в то время как регистр в " clobber " в теле " call_insn " затирается прежде, чем insn
завершает выполнение. " clobber "
выражения в этом списке увеличивают регистры, указанные в " CALL_USED_REGISTERS " (* См.: Основные сведения о регистрах::.).
- " code_label "
" code_label " insn представляет метку, на которую возможен переход при помощи insn перехода. Он содержит два специальных поля данных в дополнение к трем стандартным. " CODE_LABEL_NUMBER " используется для содержания " label number ", номер, который однозначно идентифицирует эту метку среди всех меток в трансляции (не только в текущей функции). В конечном счете,метка представляется на ассемблерном выводе как метка ассемблера, обычно в форме " LN ", где N - номер метки.
Когда " code_label " появляется в выражении RTL, оно обычно появляется внутри " label_ref ", которое представляет адрес метки как номер.
Поле " LABEL_NUSES " определяется только после завершения фазы оптимизации перехода и содержит количество ссылок на эту метку в текущей функции.
- " barrier "
Барьеры помещаются в поток команд, если управление не может передаваться коду после них. Они помещаются после команды безусловного перехода, чтобы указать, что переходы являются безусловными, и после обращений к функциям " volatile ", которые не возвращаются (например," exit "). Они не содержат никакую информацию, кроме трех стандартных полей.
- " note "
" note " insns используются для представления дополнительной отладочной и декларативной информации. Они содержат два нестандартных поля,целое число, к которому обращаются макрокомандой " NOTE_LINE_NUMBER " и строку, к которой обращаются " NOTE_SOURCE_FILE ".
Если " NOTE_LINE_NUMBER " положительно, примечание представляет номер исходной строки, а " NOTE_SOURCE_FILE " - имя исходного файла,содержащего эту строку. Эти примечания управляют генерацией номеров строк в ассемблерном выводе.
В противном случае, " NOTE_LINE_NUMBER " не реальный номер строки, а код с одним из следующих значений (а " NOTE_SOURCE_FILE " должен содержать нулевой указатель):
- " NOTE_INSN_DELETED "
Такие примечания полностью игнорируются. Некоторые проходы транслятора удаляют insns, преобразуя их в примечания этого вида.
- " NOTE_INSN_BLOCK_BEG "
- " NOTE_INSN_BLOCK_END "
Эти типы примечаний указывают позицию начала и конца уровня обзора имен переменных. Они управляют выводом информации об отладке.
- " NOTE_INSN_LOOP_BEG "
- " NOTE_INSN_LOOP_END "
Эти типы примечаний указывают позицию начала и конца циклов "while " и " for ". Они позволяют оптимизатору циклов быстро находить циклы.
- " NOTE_INSN_LOOP_CONT "
Появляется в месте цикла, на которое делает переход оператор " continue ".
- " NOTE_INSN_LOOP_VTOP "
Это примечание указывает место в цикле, где начинается проверка на выход для тех циклов, в которых проверка на выход продублирована.Эта позиция становится другим виртуальным началом цикла при рассмотрении инвариантов цикла.
- " NOTE_INSN_FUNCTION_END "
Появляется около конца тела функции, непосредственно перед меткой, на которую переходит оператор " return " (на машинах, на которых одиночной команды не достаточно для возврата). Это примечание может быть удалено оптимизацией перехода.
- " NOTE_INSN_SETJMP "
Появляется после каждого обращения к " setjmp " или соответствующей функции.
Эти коды печатаются символически, когда они появляются в отладочных дампах.
Машинный тип insn - обычно " VOIDmode ", но некоторые фазы используют тип для различных целей; например, проход перезагрузки устанавливает его в " HImode ", если insn требуется перезагрузка, но не удаление регистров,и " QImode ", если требуется и то, и другое. Проход общего удаления подвыражений устанавливает тип insn в " QImode ", если это первый insn в блоке, который уже был обработан.
Вот таблица дополнительных полей " insn ", " jump_insn " и " call_insn " insns:
- " PATTERN (I) "
Этим insn выполняется выражение c побочным эффектом. Это должен быть один из следующих кодов: " set ", " call ", " use "," clobber ", " return ", " asm_input ", " asm_output ", " addr_vec "," addr_diff_vec ", " trap_if ", " unspec ", " unspec_volatile "," parallel " или " sequence ". Если это " parallel ", каждый элемент " parallel " должен быть одним из этих кодов, за исключением того, что " parallel " выражения не могут быть вложены и " addr_vec " и " addr_diff_vec "
не разрешаются внутри выражения " parallel ".
- " INSN_CODE (I) "
Целое число, которое сообщает, какие образцы в машинном описании соответствуют этому insn, или -1, если соответствие еще не устанавливалось.
Такое соответствие никогда не устанавливается и это поле остается -1 для insn, чей образец состоит из одиночного выражения " use ", " clobber "," asm_input ", " addr_vec " или " addr_diff_vec ".
Соответствие также никогда не устанавливается для insns, которые получаются из операторов " asm ". Они содержат по крайней мере одно выражение " asm_operands ". Функция " asm_noperands " возвращает неотрицательное значение для таких insns.
При отладочном выводе это поле печатается как число с последующим символическим представлением, которое определяет месторасположение образца в " md " файле так некоторое маленькое положительное или отрицательное смещение от указанного образца.
- " LOG_LINKS (I) "
Список (цепочка выражений " insn_list ") предоставляет информацию о зависимостях между командами внутри базисного блока. Между связанным insns не может быть ни перехода, ни метки.
- " REG_NOTES (I) "
Список (цепочка выражений " expr_list " и " insn_list ") предоставляет различную информацию относительно insn. Часто это информация относительно регистров, используемых в этом insn.
Поле " LOG_LINKS " insn - это цепочка " insn_list " выражений. Каждое из них имеет два операнда: первое - insn, а второе - другое выражение " insn_list " (следующее в цепочке). Последний " insn_list " в цепочке имеет нулевой указатель в качестве второго операнда. Существенно, какие insns
появляются в цепочке как первые операнды выражений " insn_list ". Их порядок не имеет значения.
Этот список первоначально устанавливается проходом потокового анализа;
до него это нулевой указатель. Потокоый анализ только добавляет связи для тех зависимостей данных, которые могут использоваться для комбинации команд. Для каждого insn проход потокового анализа добавляет связь с insns, которые сохраняют в регистры значения, которые используются впервые в этом insn.проход планирования команд добавляет дополнительные связи так, чтобы каждая зависимость была представлена. Связи представляют зависимости данных,антизависимости и зависимости вывода; машинный тип связи различает эти три типа: антизависимости имеют тип " REG_DEP_ANTI ", зависимости вывода имеют тип " REG_DEP_OUTPUT ", и зависимости данных имеют тип " VOIDmode ".
Поле " REG_NOTES " insn - цепочка, подобная полю " LOG_LINKS ", но оно включает, помимо выражений " insn_list ", выражения " expr_list ". Имеются несколько видов регистровых примечаний,которые различаются машинным типом, который в регистровом примечании действительно понимается как являющийся " enum reg_note ". Первый операнд OP
примечания - данные, чья интерпретация зависит от вида примечания.
Макрокоманда " REG_NOTE_KIND (X) " возвращает вид регистрового примечания.Парная к ней макрокоманда " PUT_REG_NOTE_KIND (X, NEWKIND) " устанавливает тип регистрового примечания X в NEWKIND.
Регистровые примечания бывают трех классов: они могут говорить что-либо о вводе в insn, говорить что-либо о выводе insn или создавать связи между двумя insns. Имеется также набор значений, которые используются только в " LOG_LINKS ".
Эти регистровые примечания аннотируют ввод в insn:
- " REG_DEAD "
Значение в OP умирает в этом insn; то есть изменение значения немедленно после этого insn не воздействовало бы на дальнейшее поведение программы.
Это не обязательно означает, что регистр OP не содержит никакого полезного значения после этого insn, так как это может также быть вывод insn.В таком случае, однако, " REG_DEAD " примечание было бы избыточно и обычно не представляется до окончания прохода перезагрузки, но никакой код не использует этот факт.
- " REG_INC "
Регистр OP увеличивается (или уменьшается; на этом уровне не имеется никакого различия) вложенным побочным эффектом внутри этого insn. Это означает, что он появляется в выражении " post_inc ", " pre_inc "," post_dec " или " pre_dec ".
- " REG_NONNEG "
Регистр OP имеет неотрицательное значение, когда этот insn достигается.Это используется так, что команды декремента и перехода до нуля, типа m68k dbra, могут быть согласованы.
" REG_NONNEG " примечание добавляется к insns, только если машинное описание имеет образец " decrement_and_branch_until_zero ".
- " REG_NO_CONFLICT "
Этот insn не вызывает конфликта между OP и элементом, устанавливаемым этим insn, даже если имеет такую возможность.Другими словами, если регистру-адресату и OP мог бы быть иначе присвоен один и тот же регистр, этот insn не предотвращает этого присваивания.
Insns с этим примечанием - обычно часть блока, который начинается с a
" clobber " insn, указывающего псевдорегистр из нескольких слов (который будет выводом блока), группа insns, каждое из которых устанавливает одно слово значения и имеет примечание " REG_NO_CONFLICT ",и заключительное insn, которое копирует вывод к себе с примечанием " REG_EQUAL ", вычисляя выражение. Этот блок инкапсулируется примечаниями " REG_LIBCALL " и " REG_RETVAL " для первого и последнего insns, соответственно.
- " REG_LABEL "
Этот insn использует OP, " code_label ", но - не " jump_insn ".присутствие этого примечания позволяет оптимизации перехода знать что OP,на самом деле, используется.
Следующие примечания описывают атрибуты выводов insn:
- " REG_EQUIV "
- " REG_EQUAL "
Это примечание имеет силу только для insn, которое устанавливает только один регистр и указывает, что этот регистр будет равен 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 ".
- " REG_UNUSED "
Регистр OP, устанавливаемый этим insn, не будет использоваться в последующих insn. Это отличается от примечания " REG_DEAD ", которое указывает, что значение во вводе не будет использоваться впоследствии.Эти два примечания независимы и могут оба присутствовать для одного и того же регистра.
- " REG_WAS_0 "
Одиночный вывод этого insn, содержащий ноль перед этим insn.OP - insn, устанавливающий его в ноль. Вы можете полагаться на это примечание,если оно присутствует и OP не был удален или превращен в примечание ;
его отсутствие не подразумевает ничего.
Эти примечания описывают связи между insns. Они встречаются парами:одно insn имеет одно примечание, которое указывает на другое insn, которое имеет обратное примечание, указывающее обратно на первое insn.
- " REG_RETVAL "
Этот insn копирует значение последовательности из многих insn (например,вызова библиотеки), и OP - первый insn последовательности (для вызова библиотеки - первый insn, который был сгенерирован для установки параметров вызова библиотеки).
Оптимизация циклов использует это примечание для обработки такой последовательности, как одиночная операция для перемещения кода, а потоковый анализ использует это примечание, чтобы удалить последовательности, результаты которых "мертвы".
Примечание" REG_EQUAL " будет также обычно присоединяться к этому insn
для обеспечения выражения, вычисляемого последовательностью.
- " REG_LIBCALL "
Это - инверсия " REG_RETVAL ": оно помещается в первый insn последовательности из многих insn и указывает на последний.
- " REG_CC_SETTER "
- " REG_CC_USER "
На машинах, которые используют " cc0 ", insns, которые устанавливают и используют " cc0 ", установка и использование " cc0 " являются смежными.Однако по окончании заполнения слотов задержки перехода это уже может не быть так. В этом случае примечание " REG_CC_USER " будет помещено в insn, устанавливающий " cc0 ", для указания insn, использующего " cc0 ", а примечание " REG_CC_SETTER "
будет помещено в insn, использующий " cc0 ", для указания insn,устанавливающего " cc0 ".
Эти значения используются только в поле " LOG_LINKS ", и указывают тип зависимости, которую представляет каждая связь. Связи, которые указывают зависимость данных (типа зависимости чтение после записи) не используют никакого кода, они просто имеют тип " VOIDmode " и печатаются без всякого описательного текста.
- " REG_DEP_ANTI "
Указывает антизависимость (типа зависимости запись после чтения).
- " REG_DEP_OUTPUT "
Указывает зависимость вывода (типа зависимости запись после записи).
Для удобства, машинный тип в " insn_list " или " expr_list "
печатается в отладочных дампах с использованием этих символических кодов.
Единственое различие между кодами выражения " insn_list " и " expr_list " - то, что первый операнд " insn_list " считается insn и печатается в отладочных дампах как уникальный идентификатор insn;
первый операнд " expr_list " печатается обычным способом как выражение.
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 ", следуют немедленно после вызова, чтобы указать такие регистры.
Транслятор считает, что некоторые виды выражений 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 ".
Чтобы читать объект RTL из файла, вызовите " read_rtx ". Ему требуется один параметр, поток стандартного ввода, а возвращает он один объект RTL.
Чтение RTL из файла происходит очень медленно. Это не проблема в настоящее время, поскольку чтение RTL происходит только как часть формирования транслятора.
Люди часто представляют использование RTL, сохраненного как текст в файле,как интерфейс между внешним интерфейсом языка и большей частью GNU
CC. Эта идея не выполнима.
GNU CC был разработан, чтобы использовать RTL только внутренне. Корректный RTL для данной программы очень сильно зависит от конкретной целевой машины. И
RTL не содержит всей информации о программе.
Соответствующий способ связывать с помощью интерфейса GNU CC на новый внешний интерфейс языка Со структурой данных "дерева". Не имеется никакого руководства для этой структуры данных, но она описана в файлах " tree.h " и " tree.def ".