Вперед Назад Содержание

5. Машинные Описания

Машинное описание имеет две части: файл образцов команд ( ".md" файл) и файл заголовка C макроопределений.

".md" файл для целевой машины содержит образец для каждой команды, поддерживаемой целевой машиной (или, по крайней мере, для каждой команда, о которой стоит сообщать транслятору). Он может также содержать комментарии. Часть строки после точки с запятой является комментарием, если точка с запятой не находится внутри строки, ограниченной кавычками.

См. следующую главу для информации о файле заголовка C.

5.1 Все про Образцы Команд

Каждый образец команды содержит незавершенное выражение RTL, с частями, которые будут заполнены позже, ограничениями на операнды, которые говорят,как могут быть заполнены эти части, и образец вывода или C код, чтобы сгенерировать вывод ассемблера, который полностью выполняется в выражении " define_insn ".

" define_insn " является выражением RTL, содержащим четыре или пять операндов:

  1. Опциональное имя. Присутствие имени указывает, что этот образец команды может выполнять некоторую стандартную работу для проход генерации RTL транслятора. Этот проход знает определенные имена и использует образцы команд с этими именами, если имена определены в машинном описании. Отсутствие имени обозначется пустой строкой на том месте, где должно быть имя. Безымянные образцы команд никогда не используются для генерации кода RTL, но они могут позволять нескольким более простым insns объединяться впоследствии. Имена, которые не определены таким образом и используются в генерации RTL, не имеют эффекта; они эквивалентны отсутствию имени вообще.
  2. " RTL шаблон " (* См.: RTL Шаблон::.) - вектор из незавершенных выражений RTL, которые показывают, как команда должна выглядеть. Они незавершенные, потому что могут содержать " match_operand ", " match_operator " и " match_dup " выражения в позициях для операндов команды. Если вектор имеет только один элемент, этот элемент - шаблон для образца команды. Если вектор имеет несколько элементов, то образец команды - выражение " parallel ", содержащее описанные элементы.
  3. Условие. Это - строка, которая содержит выражение C, которое является тестом для окончательного решения, соответствует ли тело insn этому образцу. Для именованного образца, условие (если оно есть) не может зависеть на данных в согласовываемом insn, а может зависеть только от флагов целевой машины. Транслятор должен проверять эти условия во время инициализации, чтобы точно знать, какие поименованные команды доступны для частичного запуска. Для безымянных образцов условие применяется только тогда, когда происходит согласование для индивидуального insn, и только после установления соответствовия insn шаблону распознавания образца. Операнды insn могут быть получены из вектора " operands ".
  4. " Шаблон вывода ": строка, которая говорит, как выводить insns как код ассемблера. "%" в этой строке определяет, куда подставлять значения операнда. * См.: Шаблон Вывода::. Когда простой подстановки недостаточно, Вы можете делать вставку из кода для получения вывода. * См.: Оператор Вывода::.
  5. Опционально, вектор, содержащий значения атрибутов для insns, соответствующих этому образцу. * См.: Атрибуты Insn::.

5.2 Пример " define_insn "

Вот фактический пример образца команды для 68000/68020.

      (define_insn "tstsi"
        [(set (cc0)
              (Match_operand: SI 0 "general_operand" "rm"))]
        ""
        " *
      { if (TARGET_68020 || ! ADDRESS_REG_P (operands [0]))
          return \"tstl %0\";
        return \"cmpl *0, %0\"; }")
Это - команда, которая устанавливает условные коды, основанные на значении общего операнда. Оно не имеет условия, так что любой insn, чье RTL описание имеет указанную форму, может быть обработан согласно этому образцу. Имя "tstsi" означает " test a 'SImode' value" и сообщает проходу генерации RTL, что, когда необходимо проверить такое значение, insn для того, чтобы сделать это, можно создать, используя этот образец.

Строка управления выводом - C код, который выбирает шаблон вывода для возврата, основываясь на виде операнда и специфическом типе процессора, для которого генерируется код.

` "rm" ' является ограничением операнда. Его значение объясняется ниже.

5.3 RTL Шаблоны

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

Конструкция включает замену определенных операндов в копию шаблона. Соответствие включает определение значений, которые служат как операнды в согласовываемом insn. Оба из этих действий управляются специальными типами выражения, которые руководят соответствием и заменой операндов.

`(match_operand:M N PREDICATE CONSTRAINT)'

Это выражение - placeholder для операнда insn с номером N. При построении insn в этом месте операнд с номером N будет заменяться. При поиске соответствия insn-у, что угодно, появляющееся в этой позиции insn, будет приниматься как операнд номер N; но оно должно удовлетворять PREDICATE, или этот образец команды не будет соответствовать вообще.

Номера операндов должны быть выбраны, последовательно считая от нуля для каждого образца команды. Для каждого номера операнда в образце может иметься только одно выражение `match_operand'. Обычно операнды пронумерованы в порядке появления в выражениях `match_operand'.

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

Почти всегда PREDICATE отклонит режимы, отличные от M, но не совсем. Например, предикат 'address_operand' использует M как режим памяти, касательно которой адрес должен быть корректен. Многие предикаты принимают 'const_int', даже если их режим - 'VOIDmode' .

CONSTRAINT управляет перезагрузкой и выбором наилучшего класса регистров для хранения значений. ( Подробнее это будет объяснено ниже (см. ограничения::.) ).

Людям часто не ясны различия между ограничениями и предикатами. Предикат помогает решить, соответствует ли данный insn образцу. Ограничения не играют никакой роли в этом решении, но они управляют дальнейшими решениями в случае, когда insn соответствует.

На CISC машинах, наиболее распространенный PREDICATE - это 'general_operand'. Эта функция проверяет является ли мнимый операнд константой, регистром или ячейкой памяти, и корректен ли операнд для режима M.

Для операнда, который должен быть регистром, PREDICATE должен быть 'register_operand'. Возможно так же использование предиката 'general_operand', так как проход перезагрузки копирует любые операнды не-регистры через регистры, но это заставляло бы делать GNU CC дополнительную работу, и предотвращало бы инвариантные операнды (типа константы) от удаления из циклов, и помешало бы регистру allocator выполнять работу наилучшем способом. На RISC машинах, обычно наиболее эффективно позволить PREDICATE пропускать только те объекты, которые позволяют ограничения.

Для операнда, который должен быть константой, Вы должны убедиться, что либо Вы использовали 'immediate_operand' для PREDICATE, либо сделали дополнительное условие образца команды требующее константу, или и то и другое. Одних ограничений может быть недостаточно! Если ограничения разрешают только константы, а предикат позволяет что-то еще, то при возникновении такой ситуации транслятор разрушится.

`(match_scratch:M N CONSTRAINT)'

Это выражение - также placeholder для операнда с номером N и указывает, что операнд должен быть выражением 'scratch' или `reg' .

При соответствии образцов, это эквивалентно

           (match_operand:M N "scratch_operand" PRED)
Но, при генерации RTL, это дает выражение ( 'scratch': M ).

Если последние несколько выражения в 'parallel' являются выражениями `clobber', чьи операнды являются либо аппаратным регистром либо 'match_scratch', комбайнер может добавлять или удалять их когда необходимо.

`(match_dup N)'

Это выражение - также placeholder для операнда с номером N, оно используется, когда операнд должен появиться в insn больше чем один раз.

При конструировании, 'match_dup' действует точно так же как 'match_operand': в создаваемом insn заменяется операнд. Но в соответствии, 'match_dup' ведет себя по-другому - он принимает, что операнд номер N уже был определен ранее с помощью 'match_operand' в шаблоне распознавания, и он соответствует только идентично выглядящему выражению.

`(match_operator:M N PREDICATE [OPERANDS...])'

Этот образец - разновидность placeholder для переменного кода выражения RTL.

При построении insn, это заменяется на RTL выражение, чей код выражения взят из такового операнда N, и чьи операнды созданы из образцов OPERANDS.

При поиске соответствия, это соответствует выражению, если функция PREDICATE возвращает не ноль на это выражение *и* образцы OPERANDS соответствуют операндам выражения.

Предположим, что функция 'commutative_operator' определена следующим образом: соответствует любому выражению, чей оператор - один из коммутативных арифметических операторов RTL и чей режим - MODE:

           int
           commutative_operator (x, mode)
                rtx x;
                enum machine_mode mode;
           {
             enum rtx_code code = GET_CODE (x);
             if (GET_MODE (x) != mode)
               return 0;
             return (GET_RTX_CLASS (code) == 'c'
                     || code == EQ || code == NE);
           }
Тогда следующий образец будет соответствовать любому выражению RTL, состоящему из коммутативного оператора, примененного к двум общим операндам:

           (match_operator:SI 3 "commutative_operator"
             [(match_operand:SI 1 "general_operand" "g")
              (match_operand:SI 2 "general_operand" "g")])
Здесь вектор `[OPERANDS...]' содержат два образца, потому что все выражения, которым нужно найти соответствия, содержат два операнда.

Когда этот образец соответствует, два операнда коммутативного оператора регистрируются как операнды 1 и 2 из insn. (Это выполнено двумя 'match_operand'-ами.) Операнд 3 из insn будет полностью коммутативным выражением: используйте 'GET_CODE (операнды [3]) чтобы видеть, какой коммутативный оператор был использован.

Тип M 'match_operator'-а работает аналогично типу 'match_operand'-а: он передается как второй параметр к функции предиката, и эта функция полностью ответственна за решение, имеет ли выражение, которое будет согласовано, этот тип.

При построении insn, третий параметр gen-функции определит операцию (то есть код выражения) для выражения, которое будет сделано. Это должно быть выражение RTL, чей код выражения скопирован в новое выражение и чьи операнды - параметры 1 и 2 gen-функции. Подвыражения параметра 3 не используются; имеет значение только его код выражения.

Когда 'match_operator' используется в образце для соответствия insn, то обычно лучше всего, если номер операнда 'match_operator' выше чем номера фактических операндов insn. Это улучшает распределение регистров, потому что allocator регистров часто рассматривает операнды 1 и 2 из insns, чтобы узнать, может ли он связать эти регистры.

Не существует способа определить ограничения в 'match_operator' . Операнд insn, который соответствует match_operator' никогда не имеет никаких ограничений, потому что он никогда не перезагружается как целое. Однако, если части его OPERANDS соответствуют 'match_operand' образцам, эти части могут иметь ограничения на них самих.

`(match_op_dup:M N[OPERANDS...])'

Подобен 'match_dup', за исключением того, что применяется к операторам вместо операндов. При построении insn, операнд номер N на этом месте будет заменяться. Но в соответствии, 'match_op_dup' ведет себя по-другому. Он принимает, что операнд номер N уже был определен 'match_operator'-ом, появляющимся в шаблоне распознавания ранее, и он соответствует только идентично выглядящему выражению.

`(match_parallel N PREDICATE [SUBPAT...])'

Этот образец - placeholder для insn, который состоит из выражения `parallel' с переменным числом элементов. Это выражение может появиться только на верхнем уровне insn образца.

При построении insn, операнд номер N будет заменяться на этом месте. При соответствии insn, это соответствует, если тело insn - выражение `parallel' с по крайней мере таким количеством элементов, как вектор выражений SUBPAT в 'match_parallel' , если каждый SUBPAT соответствует соответствующему элементу 'parallel', *и* функция PREDICATE возвращает не ноль на 'parallel', который является телом insn. За корректность элементов 'parallel', которые не указаны в `match_parallel', несет ответственность предикат.

Обычно `match_parallel' используют, чтобы сравнивать его с многочисленными выражениями чтения и записи, которые могут содержать различное число элементов в 'parallel'. Например:

           (define_insn ""
             [(match_parallel 0 "load_multiple_operation"
                [(set (match_operand:SI 1 "gpc_reg_operand" "=r")
                      (match_operand:SI 2 "memory_operand" "m"))
                 (use (reg:SI 179))
                 (clobber (reg:SI 179))])]
             ""
             "loadm 0,0,%1,%2")
Этот пример взят из 'a29k.md'. Функция 'load_multiple_operations' определена в 'a29k.c' и проверяет, что последующие элементы в `parallel' те же, что и `set' в образце, за исключением того, что они ссылаются на последующие регистры и ячейки памяти.

Insn, который соответствует этому образцу, может выглядеть например так:

           (parallel
            [(set (reg:SI 20) (mem:SI (reg:SI 100)))
             (use (reg:SI 179))
             (clobber (reg:SI 179))
             (set (reg:SI 21)
                  (mem:SI (plus:SI (reg:SI 100)
                                   (const_int 4))))
             (set (reg:SI 22)
                  (mem:SI (plus:SI (reg:SI 100)
                                   (const_int 8))))])
`(match_par_dup N [SUBPAT...])'

Подобно 'match_op_dup', но для 'match_parallel' вместо 'match_operator'.

`(address (match_operand:M N "address_operand" ""))'

Этот комплекс выражений - placeholder для операнда номер N в инструкции "загрузки адреса": операнд, который определяет ячейку памяти обычным способом, но для которого фактическое значение операнда есть адрес ячейки, а не ее содержание.

Выражения `address' никогда не появляются в RTL коде, только в машинных описаниях. И они используются только в машинных описаниях, которые не используют особенности ограничения операнда. Когда ограничения операнда используются, символ 'p' в ограничении служит этой цели.

M - тип *адресуемой ячейки памяти*, а не тип адреса. Этот тип всегда один и тот же на данной целевой машине ( 'Pmode' , который обычно является 'SImode'), так что не имеет смысла это упоминать; таким образом, в выражении 'address' не написан никакой тип. Если однажды будет добавлена поддержка машин, в которых адреса объектов различного вида появляются по-разному или используются по-разному (типа PDP-10), различные форматы, возможно, будут нуждаться в различных типах, и эти типы должны быть упомянуты в выражении 'address'.

5.4 Шаблоны вывода и замена операндов

'Шаблон вывода' - это строка, которая определяет, как выводить ассемблерный код для образца команды. Почти весь шаблон - фиксированная строка, которая выводится буквально. Символ '%' используется, чтобы определить место замены операндом; он может также использоваться, чтобы обозначать места, где различные варианты ассемблера требуют различного синтаксиса.

В самом простом случае, '%', а за ним число N указывает, что надо вывести операнд N в этом месте.

'%' после которого стоит символ и цифра говорит, что надо вывести операнд альтернативным способом. Четыре символа имеют стандартные, встроенные значения, описанные ниже. Машинная макрокоманда описания 'PRINT_OPERAND' может определять дополнительные символы с нестандартными значениями.

'%cDIGIT' может использоваться для замены операнда, который является постоянным значением без синтаксиса, который обычно указывает непосредственный операнд.

'%nDIGIT' тоже, что и '%cDIGIT' , но значение константы перед печатью заменяется на минус ее.

'%aDIGIT ' может использоваться, чтобы заменить операнд, так как если бы это была ссылка на ячейку памяти, фактическим операндом, обрабатываемым как адрес. Это может быть полезно, когда выводится инструкция "загрузить адрес", потому что часто синтаксис ассемблера для такой команды требует, чтобы Вы писали операнд, как будто это была бы ячейка память.

'%lDIGIT ' используется, чтобы заменить ' label_ref ' на команду перехода.

'%=' выводит уникальный номер для каждой команды во всей трансляции. Это полезно для создания локальных меток, которые нужно упомянуть в шаблоне больше чем один раз, генерирующем многократные команды ассемблера.

'%' за которым следует символ пунктуации определяет замену, не использующую операнд. Стандартным является только один случай : ' %% ' - выводит в код ассемблера '%' . Другие, нестандартные случаи могут быть определены в макрокоманде 'PRINT_OPERAND' . Вы также должны определить, какие символы пунктуации корректны, в макрокоманде 'PRINT_OPERAND_PUNCT_VALID_P' .

Шаблон может генерировать несколько команд ассемблера. Пишите текст для таких команд, разделяя их ' \; ' .

Когда RTL содержит два операнда, которые из-за ограничения должны соответствовать друг другу, шаблон вывода должен обратиться только к тому операнду, номер которого меньше. Соответствующие операнды не всегда идентичны, и остальная часть транслятора работает так, чтобы поместить соответствующее выражение RTL для печати в операнд с меньшим номером.

Используя нестандартные символы или пунктуацию после '%', нужно проводить различие между различными языками ассемблеров для той же самой машины; например, синтаксисом Motorola и синтаксисом MIT для 68000. Синтаксис Motorola требует точек в большинстве имен кода операции, в то время как MIT синтаксис не требует этого. Например, код операции 'movel' в MIT синтаксисе - 'move.l' в Motorola синтаксисе. Для обоих видов синтаксиса вывода используется один и тот же файл образцов, но в каждом месте, где Motorola синтаксис хочет точку, используется символьная последовательность '%'. Макрокоманда PRINT_OPERAND для Motorola синтаксиса определяет последовательность, чтобы выводить точку; макрокоманда для MIT синтаксиса определяет, что не надо ничего выводить.

Как специальный случай, шаблон, состоящий из одиночного символа '*' дает инструкцию транслятору, чтобы сначала разбить insn, а затем вывести полученные команды по отдельности. Это помогает устранять избыточность в шаблонах вывода. Если Вы имеете 'define_insn' который должен создать несколько команд ассемблера, и имеется уже определенное соответствие - 'define_split' , то Вы можете просто использовать '*' как шаблон вывода, вместо того, чтобы создавать шаблон вывода, генерящий несколько ассемблерных команд.

Если 'ASSEMBLER_DIALECT' определен, Вы можете использовать в шаблонах конструкции типа '{option0 | option1 | option2} '. Они описывают множество вариантов синтаксиса языка ассемблера. см. Вывод команд

5.5 Операторы С для вывода ассемблера

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

Если строка управления вывода начинается с '@' , то это - ряд шаблонов, каждый на отдельной строке. (Пустые строки, ведущие пробелы и знаки табуляции игнорируются.) Шаблоны соответствуют вариантам ограничения образца (см. Множественные альтернативы). Например, если целевая машина имеет двухадресную команду сложения 'addr' для сложения в регистр и другую 'addm' , чтобы добавить регистр к памяти, то Вы можете использовать этот образец:

      (define_insn "addsi3"
        [(set (match_operand:SI 0 "general_operand" "=r,m")
              (plus:SI (match_operand:SI 1 "general_operand" "0,0")
                       (match_operand:SI 2 "general_operand" "g,r")))]
        ""
        "@
         addr %2,%0
         addm %2,%0")
Если строка управления вывода начинается с '*' , то далее идет не шаблон вывода, а часть программы C, которая должна вычислить шаблон. Она должна использовать оператор 'return' , чтобы возвратить строку шаблона, которую Вы хотите. Большинство таких шаблонов использует строчные литералы C, которые должны быть разграничены символами двойных кавычек. Перед каждым символом двойных кавычек надо ставить '\' .

Операнды могут быть найдены в массиве 'operands' ,тип данных которого в С- ' rtx [] ' .

Общепринято выбирать различные способы генерирования ассемблерного кода, базирующихся на том, находится ли непосредственный операнд внутри некоторого диапазона. Будьте внимательным при выполнении этого, потому что результат 'INTVAL' на главной машине является целым числом. Если главная машина имеет большее количество битов в 'int' чем целевая машина имеет в типе, в котором константа будет использоваться, то некоторые из битов, которые Вы получаете из 'INTVAL', будут лишними. Для правильных результатов, Вы должны тщательно игнорировать значения этих битов.

Возможно вывести команду ассемблера а затем продолжать выводить или вычислять остальные команды, используя подпрограмму 'output_asm_insn'. Она получает два параметра: строка шаблона и вектор операндов. Вектор может быть 'operands', или это может быть другой массив 'rtx' который Вы объявляете локально и инициализируете самостоятельно.

Когда образец insn имеет несколько вариантов ограничений, часто появление ассемблерного кода определяется в основном тем, какой из вариантов был согласован. Когда это - так, C код может проверять переменную 'which_alternative ', являющуюся порядковым номером варианта, который был согласован первым (0 для первого, 1 для второго варианта, и т.д.).

Например, предположим, что имеются два кода операции для сохранения нуля, 'clrreg ' для регистров и 'clrmem' для ячеек памяти. Ниже приведен пример, как лбразец может использовать 'which_alternative ' для того, чтобы выбрать между ними:

      (define_insn ""
        [(set (match_operand:SI 0 "general_operand" "=r,m")
              (const_int 0))]
        ""
        "*
        return (which_alternative == 0
                ? \"clrreg %0\" : \"clrmem %0\");
        ")
В вышеприведенном примере ассемблерный код *полностью* определялся выбранной альтернативой, но он мог быть также определен через строку управления выводом начинающуюся с '@' следующим образом:

      (define_insn ""
        [(set (match_operand:SI 0 "general_operand" "=r,m")
              (const_int 0))]
        ""
        "@
         clrreg %0
         clrmem %0")

5.6 Ограничения операндов

Каждый 'match_operand' в образце команды может определять ограничение на тип операндов. Ограничения могут указывать, может ли операнд быть в регистре, и в каких видах регистров; может ли операнд быть ячейкой памяти, и каких видов адрес; может ли операнд быть непосредственной константой, и какие возможные значения он может иметь. Ограничения могут также требовать соответствия между двумя операндами.

Простые Ограничения

Самый простой вид ограничения - строка, заполненная символами, каждый из которых описывает один вид разрешенного операнда. Имеются следующие разрешенные символы:

`m'

Разрешается операнд памяти с любым видом адреса, который машина вообще поддерживает.

`o'

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

Например, адрес, который является постоянным - смещаемый; поэтому адрес, который является суммой регистра и константы тоже смещаемый (если только небольшое увеличение константы оставляет адрес внутри диапазона, поддерживаемого машиной); но самоувеличивающийся или самоуменьшающийся адрес - не смещаемый. Более сложные косвенные/индексированные адреса могут быть или не быть смещаемыми в зависимости от других способов адресации, которые поддерживает машина.

Обратите внимание на то, что в операнде вывода, который может быть согласован с другим операндом, символ ограничения 'o' корректен только когда сопровождает оба операнда : '<' (если целевая машина имеет увеличение адреса) и '>' (если целевая машина имеет уменьшение адреса).

`V'

Не смещаемый операнд памяти. Другими словами, любой операнд, удовлетворяющий 'm' ограничению, но не 'o' ограничению.

`<'

Разрешаются операнды памяти с автоматической декрементацией адреса (либо преддекрементацией либо последекрементацией).

`>'

Разрешаются операнды памяти с автоматической инкрементацией адреса (либо прединкрементацией либо постинкрементацией).

`r'

Разрешается операнд регистра, при условии, что он находится в общем регистре.

`d', `a', `f', ...

Другие символы могут быть определены машинно-зависимым способом вместо конкретных классов регистров. 'D' , 'a' и 'f' определены на 68000/68020, для регистров данных, регистров адреса и регистров с плавающей точкой соответственно.

`i'

Разрешается непосредственный целочисленный операнд (с постоянным значением). Сюда так же включаются символьные константы, чьи значения будут известны только во время трансляции.

`n'

Разрешается непосредственный целочисленный операнд с известным числовым значением . Большинство систем не поддерживают константы, созданные во время трансляции, для операндов, меньших чем слово. Ограничения для этих операндов должны использовать скорее 'n' чем 'i' .

`I', `J', `K', ... `P'

Другие символы в диапазоне от 'I' до 'P' могут быть определены машинно-зависимым способом для разрешения непосредственных целочисленных операндов с явными целочисленными значениями в определенных диапазонах. Например, на 68000, 'I' определен для диапазона значений от 1 до 8. Это диапазон, разрешенный для размера сдвига в командах сдвига.

`E'

Разрешен непосредственный операнд с плавающей точкой (код выражения 'const_double' ), но только, если формат типа с плавающей точкой на целевой машине совпадает с соответствующим форматом на главной машине.

`F'

Разрешается непосредственный операнд с плавающей точкой (код выражения 'const_double').

`G', `H'

'G' и ' H' могут быть определены машинно-зависимым способом, чтобы разрешать непосредственные вещественные операнды в конкретных диапазонах значений.

`s'

Разрешается непосредственный целочисленный операнд, чье значение не является явно заданным целым число.

Это может показаться странным; если insn разрешает постоянный операнд со значением, не известным во времени компиляции, то конечно же он должен позволять и любое известное значение. Так почему используется 's' вместо 'i'. Иногда это позволяет сгенерировать лучший код.

Например, на 68000 в fullword команде возможно использовать непосредственный операнд; но если непосредственное значение - между -128 и 127, лучший код получится, если загрузить значение в регистр и использовать регистр. Это происходит из-за того, что загрузка в регистр может быть выполнена с помощью команды 'moveq'. Мы предусмотрели это, определив, что символ 'K' означает любое целое число вне диапазона от -128 до 127 и тогда записав 'Ks' в ограничения операнда.

`g'

Разрешается любой регистр, память или непосредственный целочисленный операнд, кроме регистров, которые не являются общими.

`X'

Разрешается любой операнд, даже если он не удовлетворяет 'general_operand'. Обычно это используется в ограничении 'match_scratch ' когда некоторые варианты фактически не будут требовать рабочего регистра.

`0', `1', `2', ... `9'

Разрешается операнд, который соответствует определенному номеру операнда. Если цифра используется вместе с символами внутри того же самого варианта, то цифра должна стоять последней.

Это называется 'соответствие ограничения' и это действительно означает то, что ассемблер имеет только один операнд, который выполняет две роли, рассматриваемые отдельными в RTL insn. Например, insn сложения имеет два входных операнда и один операнд вывода в RTL, но на большинстве CISC машин команда сложения в действительности имеет только два операнда, один из них операнд ввода-вывода:

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

То, что операнды соответствуют друг другу, в конкретном случае означает, что они идентично выглядящие выражения RTL. Но в нескольких специальных случаях разрешаются определенные виды различий. Например, '*x' как входной операнд будет соответствовать '*x++' как операнд вывода. Для правильных результатов в таких случаях, шаблон вывода должен всегда использовать номер операнда вывода при печати операнда.

`p'

Разрешается операнд, который является корректным адресом памяти. Это нужно для инструкций "load address" и "push address".

'P' в ограничении должен сопрвождаться 'address_operand' как предикат в 'match_operand'. Этот предикат интерпретирует тип, определенный в 'match_operand' как тип ячейки памяти, для которой адрес был бы корректным.

`Q', `R', `S', ... `U'

Символы в диапазоне от 'Q' до 'U' могут быть определены машинно-зависимым способом вместо произвольных типов операнда. Машинной макрокоманде описания 'EXTRA_CONSTRAINT' передается операнд как первый параметр и символ ограничения как второй операнд.

Типичное использование для этого - различение некоторых типы ссылок памяти, которые воздействуют на другие insn операнды.

Не определяйте эти символы ограничения, чтобы принять ссылки регистра ('reg'); проход перезагрузки не ожидает этого и не обработает это правильно.

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

Сравните два следующих образца:

      (define_insn ""
        [(set (match_operand:SI 0 "general_operand" "=r")
              (plus:SI (match_dup 0)
                       (match_operand:SI 1 "general_operand" "r")))]
        ""
        "...")
который имеет два операнда, один из которых должен появиться в двух местах, и

      (define_insn ""
        [(set (match_operand:SI 0 "general_operand" "=r")
              (plus:SI (match_operand:SI 1 "general_operand" "0")
                       (match_operand:SI 2 "general_operand" "r")))]
        ""
        "...")

который имеет три операнда, два из которых по требованию ограничения должны быть идентичны. Если мы рассматриваем insn вида

      (insn N PREV NEXT
        (set (reg:SI 3)
             (plus:SI (reg:SI 6) (reg:SI 109)))
        ...)
первый образец не применился бы вообще, потому что этот insn не содержит два идентичных подвыражения в нужном месте. Образец сказал бы "Это не похоже на команду сложения; попробуйте другие образцы." Второй образец сказал бы "Да, это команда сложения, но с ней что-то не так." Он направил бы проход перезагрузки транслятора, чтобы сгенерировать дополнительные insns, делающие ограничения истинными. Результаты могли бы выглядеть следующим образом:

      (insn N2 PREV N
        (set (reg:SI 3) (reg:SI 6))
        ...)
 
      (insn N N2 NEXT
        (set (reg:SI 3)
             (plus:SI (reg:SI 3) (reg:SI 109)))
        ...)
Это для того, чтобы удостовериться что каждый операнд, в каждом образце, имеет ограничения, которые могут обрабатывать любое выражение RTL, которое могло бы присутствовать для того операнда. (Когда используются множественные варианты, каждый образец для каждой возможной комбинации выражений операнда должен иметь по крайней мере один вариант, который может обрабатывать ту комбинацию операндов.) Ограничения не должны *позволять* любой возможный операнд, но они должны по крайней мере указать путь на перезагрузку любого возможного операнда так, чтобы он удовлетворил ограничениям.
  • Если ограничение принимает любые операнды, которые разрешает предикат, то не будет никаких проблем: перезарядка для этого операнда никогда не потребуется. Например, операнд, чьи ограничения разрешают все за исключением регистраторов, безопасно обеспечит свой предикат отклонением регистров. Операнд, чьи вводы предиката только постоянные значения, безопасно обеспечит ограничения, включая символ 'i' . Если принято любое возможное постоянное значение, то ничего меньшее 'i' не пропустится; если предикат более выборочный, то ограничения могут также быть более выборочные.
  • Любое выражение операнда может быть перезаряжено, с помощью копирования в регистр. Так, если ограничения операнда позволяют некоторый регистр, то это заведомо безопасно. Требуется разрешить все классы регистраторов; транслятор знает, как копировать регистр в другой регистр соответствующего класса, чтобы делать корректные команды.
  • Несмещаемая ячейка памяти может быть перезаряжена, копируя адрес в регистр. Так, если ограничение использует символ 'o', то все ячейки будут перезаряжены.
  • Постоянный операнд может быть перезагружен с помощью выделения места в памяти, чтобы держать его как преинициализированные данные. Поэтому вместо константы может быть использована ссылка на память. Так, что если ограничения содержит символ `o' или `m', то константные операнды так же проходят.
  • Если ограничение разрешает константу, и псевдорегистр, используемый в insn, который может быть расположен не в аппаратном регистре и поэтому эквивалентен константе, то регистр будет заменен на константу. Если предикат не разрешает константу, а insn по некоторым причинам повторно распознан, транслятор разрушится. Таким образом предикат должен всегда распознавать любые объекты, позволенные ограничением.
Если предикат операнда пропускает регистры, а ограничение не разрешает их, то это может привести к аварийному отказу транслятора. Когда этот операнд регистр, проход перезагрузки будет stymied, потому что он не знает, как временно копировать регистр в память.

Многократные Альтернативные Ограничения

Иногда одиночная команда имеет много альтернативных наборов возможных операндов. Например, на 68000, команда логического or может объединять регистр и непосредственное значение памяти, так же она может объединять операнд любого вида в регистр; но она не может объединять одну ячейку памяти с другой.

Эти ограничения представляются несколькими вариантами. Вариант может быть описан рядом символов для каждого операнда. Полное ограничение для операнда сделано из символов для этого операнда из первого варианта, запятой, символы для этого операнда из варианта второго, запятой, и так далее до последнего варианта. Далее показывается как это делается для логического или с полными словами на 68000:

      (define_insn "iorsi3"
        [(set (match_operand:SI 0 "general_operand" "=m,d")
              (ior:SI (match_operand:SI 1 "general_operand" "%0,0")
                      (match_operand:SI 2 "general_operand" "dKs,dmKs")))]
        ...)
Первый вариант имеет 'm' (память) для операнда 0, '0' для операнда 1 (его значение должно соответствовать операнду 0), и 'dKs' для операнда 2. Другой вариант имеет 'd' (регистр данных) для операнда 0, '0' для операнда 1, и 'dmKs' для операнда 2. '=' и '%' в ограничениях обращаются ко всем вариантам; их значение объясняется в следующем разделе (см. Предпочтения классов).

Если все операнды удовлетворяют любому варианту, команда корректна. Иначе, для каждого варианта, транслятор рассчитывает, сколько команд должны быть добавлены, чтобы скопировать операнды таким образом, чтобы применился этот вариант. Будет выбран вариант, требующий наименьшего копирования. Если двум вариантам требуется одинаковое количество копирования, будет выбран первый. Эти выборы могут быть изменены с помощью символов '?'и '!':

`?'

Немного понижает приоритет варианта, в котором появляется. Работает в случае, когда не один из вариантов точно не выбран. Транслятор расценивает такой вариант как более приоритетный.

`!'

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

Если образец insn имеет множество вариантов ограничений, то часто появление ассемблерного кода в основном определяется тем, какой вариант был выбран. Когда это - так, C код для записи кода ассемблера может использовать переменную 'which_alternative' , которой является порядковым номером варианта, который был фактически удовлетворен (0 для первого, 1 для варианта второго, и т.д.). см. Оператор вывода

Предпочтения классов регистров

Ограничения операнда служат для других целей: они дают возможность транслятору решить, как лучше всего использовать аппаратные и псевдо регистры. Транслятор исследует ограничения, которые обращаются к insns, использующие псевдо регистр, ища машинно-зависимые символы типа 'd' и 'a' которые определяют классы регистраторов. Псевдо регистр помещен в любой класс становит наиболее голосующим. Символы ограничения 'g' и 'r' также голосуют: они голосуют в пользу общего регистра. В машинном описании говорится, какие регистры являются общими.

Конечно, на некоторых машинах все регистры эквивалентны, и нет никаких классов регистров. И тогда все что описано ниже не нужно.

Символы модификатора ограничения

Далее идут символы модификатора ограничения.

`='

Означает, что этот операнд в этой команде только для чтения: предыдущее значение отбрасывается и заменяется данными результата.

`+'

Означает, что этот операнд инструкция может как читать так и писать.

Когда транслятор устанавливает операнды, чтобы удовлетворить ограничению, требуется знать, какие операнды являются вводами к команде а какие являются выводами из нее. '=' идентифицирует вывод; '+' идентифицирует операнд, который является, и вводом и выводом; все другие операнды только входные.

`&'

Означает (в отличии от других случаев) что этот операнд записан прежде, чем команда завершена, используя входные операнды. Следовательно, этот операнд не может находиться в регистре, используемом как входной операнд или как часть любого адреса памяти.

'&' применяется только к варианту, в котором это написано. В ограничениях с многократными вариантами, иногда один вариант требует '&' в то время как другие нет. См. например, 'movdf' insn из 68000.

'&' не устраняет потребность писать '=' .

`%'

Объявляет, что команда будет коммутативна для этого и следующего операндов. Это означает, что транслятор может обмениваться двумя операндами, если это самый дешевый способ заставить все операнды удовлетворять ограничению. Это часто используется в образцах для команды сложения, которая действительно имеют только два операнда: результат должен войти в один из параметров. Здесь, например, описано как определена инструкция сложения полуслов в 68000:

           (define_insn "addhi3"
             [(set (match_operand:HI 0 "general_operand" "=m,r")
                (plus:HI (match_operand:HI 1 "general_operand" "%0,0")
                         (match_operand:HI 2 "general_operand" "di,g")))]
             ...)
`#'

Говорит, что весь последующие символы, вплоть до следующей запятой, как ограничение должны игнорироваться. Эти символы значимы только для выбор предпочтительного регистра.

`*'

Говорит, что следующий символ при выборе предпочтительного регистра должен игнорироваться. '*' не оказывает никакого эффекта на значение ограничения, перезарядку.

Далее идет пример: 68000 имеет команду для знакового распространения половины слова в регистр данных, и возможно также знаковое распространение с помощью копирования в регистр адреса. В то время как любой вид регистра приемлем, ограничения на адресата регистра адреса менее строги, так что самое лучшее, если распределение регистра заставляет адрес регистрировать цель. Следовательно, '*' используется так, чтобы символ ограничения 'd' (для регистра данных) игнорировался при вычислении предпочтения регистра.

           (define_insn "extendhisi2"
             [(set (match_operand:SI 0 "general_operand" "=*d,a")
                   (sign_extend:SI
                    (match_operand:HI 1 "general_operand" "0,g")))]
             ...)

Ограничения для Специфических Машин

По мере возможности, Вы должны пытаться использовать в параметрах 'asm' ограничения общего назначения, т.к. это легче восприниматься другими людьми. Если Вы этого не умеете, используйте символы ограничения, которые имеют очень много разных конкретных значений для разных архитектур. Наиболее часто используются: `m' и `r' (для памяти и регистров общего назначения соответственно; см. Простые ограничения), и `I', обычно символ, указывающий наиболее общий формат уже полуученого числа.

Для каждой архитектуры машины, файл 'config/MACHINE.h' определяет дополнительные ограничения. Эти ограничения используются транслятором непосредственно для генерации команд, также как для asm-операторов; следовательно, некоторые из ограничений не особенно интересны для asm. Ограничения определены через следующие макрокоманды:

`REG_CLASS_FROM_LETTER'

Ограничения типа регистра (обычно строчные буквы).

`CONST_OK_FOR_LETTER_P'

Непосредственные постоянные ограничения, для констант с неплавающей точкой размером слова или меньше (обычно верхний регистр).

`CONST_DOUBLE_OK_FOR_LETTER_P'

Непосредственные постоянные ограничения, для всех констант с плавающей точкой и для констант больших чем точность размера слова (обычно верхний регистр).

`EXTRA_CONSTRAINT'

Специальные случаи регистров или памяти. Эта макрокоманда не обязательна, и определена только для некоторых машин.

Проверка этих макроопределений в исходниках транслятора для вашей машины - самый лучший способ убедиться, что Вы имеете правильные ограничения. Как бы то ни было, здесь дано резюме машинно-зависимых ограничений, доступных на некоторых специфических машинах.
*семейство ARM --`arm.h'*

`f'

регистр с плавающей точкой

'F'

Одна из следующих констант с плавающей точкой - 0.0, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0 или 10.0

'G'

Константа с плавающей точкой, которая удовлетворила бы ограничению 'F' если поменять у нее знак

'I'

Целое число, которое может использоваться как непосредственный операнд в команде обработки данных. То есть целое число в диапазоне от 0 до 255 с вращаемым множителем 2

'J'

Целое число в диапазоне -4095 к 4095

'K'

Целое число, которое удовлетворяет ограничение 'I' если изменить знак (одинарное дополнение)

'L'

Целое число, которое удовлетворило бы ограничению 'I' если поменять у него знак (двойное дополнение)

'M'

Целое число в диапазоне 0 к 32

'Q'

Ячейка памяти, где точный адрес находится в одном регистре ( ``m'' является предпочтительной для `asm'-операнда )

'R'

Элемент constant pool

'S'

Символ в текстовом сегменте текущего(актуального) файла

*AMD 29000 family--`a29k.h'*

'l'

Локальный регистр 0

'B'

Регистр указателя байта ('BP')

'q'

'Q' регистр

'H'

Специальный регистр цели

`A'

Первый регистр сумматор

`a'

Другой сумматор

'f'

регистр с плавающей точкой

'I'

Константа большая чем 0, меньшая чем 0x100

'J'

Константа большая чем 0, меньшая чем 0x10000

'K'

Константа, чьи верхние 24 бита находятся на (1)

'L'

16 битная константа, чьи верхние 8 битов находятся на (1)

'M'

32 битная константа, чей верхние 16 битов находятся на (1)

'N'

32 битная отрицательная константа, которая помещается в 8 бит

'O'

Константа 0x80000000 или, на 29050, любые 32 разрядных константа, чей первые 16 битов - 0.

'P'

16 битная отрицательная константа, которая помещается в 8 бит.

'G'

'H'

Константа с плавающей точкой (в asm-операторах, используйте взамен машинно-независимые 'E' или 'F')

*IBM RS6000--`rs6000.h'*

'b'

Регистр основы Адреса

'F'

регистр с плавающей точкой

'H'

'MQ' , 'CTR' , или 'LINK' регистр

'Q'

'MQ' регистр

'C'

'CTR' регистр

'L'

'LINK' регистр

'X'

'CR' регистр номер 0 (регистр условия)

'Y'

'CR' регистр (регистр условия)

'I'

16-битная константа со знаком

'J'

Константа, чьи первые 16 бит - 0

'K'

Константа, чьи верхние 16 бит - 0

'L'

Константа подходящий как операнд маски

'M'

Константа больше чем 31

'N'

Точная мощность 2

'O'

Ноль

'P'

Константа, у которой если поменять знак, то получится 16 битная знаковая константа

'G'

Константа с плавающей точкой, которая может быть загружена в регистр с помощи одной команды на слово

'Q'

Операнд Памяти, который является смещением из регистра ( 'm' является предпочтительным для `asm'-операторов )

*Intel 386--`i386.h'*

'q'

'a' , 'b' , 'c' , или 'd' регистр

'A'

'a' , или 'd' регистр (для ints с 64 битами)

'f'

регистр с плавающей точкой

'T'

Первый регистр с плавающей точкой (начало стека)

'U'

Второй регистр с плавающей точкой

`a'

`a' регистр

`b'

`b' регистр

`c'

`c' регистр

`d'

`d' регистр

`D'

`di' регистр

`S'

`si' регистр

'I'

Константа в диапазоне 0 к 31 (для 32 разрядных сдвигов)

'J'

Константа в диапазоне 0 к 63 (для 64 разрядных сдвигов)

'K'

'0xff'

'L'

'0xffff'

'M'

0, 1, 2, или 3 (сдвигается c помощью 'lea' команды)

'G'

Стандартная 80387 константа с плавающей точкой

*Intel 960--`i960.h'*

'f'

регистр с плавающей точкой ( ' fp0 ' к ' fp3 ' )

'L'

Локальный регистр ( ' r0 ' к ' r15 ' )

'b'

Глобальный регистр ( ' g0 ' к ' g15 ' )

'd'

Любой - локальный или глобальный регистр

'I'

Целые числа от 0 до 31

'J'

0

'K'

Целые числа от -31 до 0

'G'

вещественный (с плавающей точкой) 0

'H'

вещественный (с плавающей точкой) 1

*MIPS--`mips.h'*

'D'

Целочисленный регистр общего назначения

'F'

регистр с плавающей точкой (если доступен)

'H'

'Hi' регистр

'L'

'Lo' регистр

'X'

'Hi' или 'Lo' регистр

'Y'

Целочисленный регистр общего назначения

'Z'

регистр состояния операций и команд с плавающей точкой

'I'

16 разрядная константа со знаком (для арифметических команд)

'J'

Ноль

'K'

Расширенная нулем 16-битная константа (для логических команд)

'L'

Константа у которой нижние 16 бит - 0 (может быть загружена ' lui ' )

'M'

32 битная константа, которая требует, две команды для загрузки (константа которая - не `I', `K', или `L' )

'N'

Отрицательная 16-битная константа

'O'

Точная мощность 2

'P'

Положительная 16-битная константа

'G'

Вещественный (с плавающей точкой) 0

'Q'

Ячейка памяти памяти, которая может быть загружена более чем одной командой ( 'm' более предпочтительно для asm-операторов)

'R'

Ячейка памяти, которая может быть загружена одной командой ( 'm' более предпочтительно для asm-операторов)

'S'

Ячейка памяти во внешнем формате OSF/rose PIC ( 'm' более предпочтительно для asm-операторов)

*Motorola 680x0--`m68k.h'*

`a'

Регистр Адреса

'D'

Регистр Данных

'F'

68881 регистр числа с плавающей точкой, если доступен

'X'

Sun FPA (вещественный, с плавающей точкой) регистр, если доступен

'Y'

Первые 16 Sun FPA регистров, ксли доступны

`I'

Целое число в диапазоне от 1 до 8

`J'

16 битное знаковое число

'K'

Знаковое число, чья величина большая чем 0x80

'L'

Целое число в диапазоне от -8 до -1

'G'

Константа с плавающей точкой, не являющаяся 68881 константой

'H'

Константа с плавающей точкой, которая может использоваться Sun FPA

*SPARC--`sparc.h'*

'F'

регистр с плавающей точкой

`I'

Знаковая 13-битная константа

'J'

Ноль

'K'

32 битная константа с пустыми нижними 12 битами (константа, которая может быть загружена командой 'sethi')

'G'

Вещественный (с плавающей точкой) 0

'H'

Знаковая 13 битная константа, знако-распространенная до 32 или 64 бит

'Q'

Ячейка памяти, которая может быть загружена одной командой ( 'm' более предпочтительно для asm-операторов)

'S'

Константа, или адрес памяти

'T'

Адрес памяти, выровненный к 8 байтовой границе

'U'

Просто регистр

Без ограничений

Некоторые машины настолько чисты, что ограничения на операнды просто не нужны. Например, на Vax операнд, корректный в одном контексте, будет так же корректен и в любом другом контексте. На таких машинах каждый операнд должен иметь ограничение `g', исключая, разве что, операнды инструкций загрузки адреса, которые написаны, как будто они обращаются к ячейкам памяти, но фактический они обращаются к адресу ячейки. Они имели бы ограничение 'p'.

Для таких машин, вместо того, чтобы записывать 'g' и 'p' для всех ограничений, Вы можете писать описание с пустыми ограничениями. Тогда Вы пишете `""' для ограничения в каждом 'match_operand' . Операнды Адреса идентифицируются, записыванием выражения `address' рядом с 'match_operand', а не их ограничениями.

Когда машинное описание имеет только пустые ограничения, некоторые части трансляции будут пропущены, что делает транслятор быстрым. Однако, только очень малое число машин фактически не нуждаются в ограничениях; все машинные описания теперь уже включают ограничения использования.

5.7 Стандартные Образцы Имен Для Генерации

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

" movM "

Здесь M - имя машинного типа из двух символов, строчными буквами. Этот образец команды перемещает данные с этим машинным типом из операнда 1 в операнд 0. Например, " movsi " перемещает данные длиной в слово.

Если операнд 0 - " subreg " с типом M регистра, чей собственный тип более широкий, чем M, эффект этой команды состоит в сохранении указанного значения в части регистра, соответствующей типу M. Эффект на остальной части регистра не определен.

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

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

Следовательно, когда задана такая пара операндов, образец должен генерировать RTL, который не нуждается в перезагрузке и не нуждается во временных регистрах - никаких регистрах, отличных от операндов. Например, если Вы поддерживаете образец с " define_expand ", то в таком случае " define_expand " не должен вызывать " force_reg " или любую другую такую функцию, которая могла бы генерировать новые псевдорегистры.

Это требование существует даже для типов подслова на RISC машине, на которой при выборке этих типов из памяти обычно требуется несколько insns и несколько временные регистров. Посмотрите в " spur.md ", чтобы увидеть как это требование может быть удовлетворено.

В течение перезагрузки ссылка памяти с недопустимым адресом может быть передана как операнд. Такой адрес будет заменен на допустимый адрес позже в проходе перезагрузки. В этом случае с адресом нельзя делать ничего, за исключением использования его так, как это имеет место. Если он копируется, он не заменяется на имеющий силу адрес. Не должно делаться никакой попытки сделать такой адрес допустимым и нельзя вызывать подпрограмму (типа " change_address "), которая будет это делать. Обратите внимание, что " general_operand " потерпит неудачу, если применить его к такому адресу.

Глобальная переменная " reload_in_progress " (которая должна быть явно объявлена, если требуется) может использоваться, чтобы определить, требуется ли такая специальная обработка.

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

Если рабочий регистр требуется, чтобы переместить объект в память или из памяти, он может быть распределен с использованием " gen_reg_rtx " до перезагрузки. Но это невозможно в течение и после перезагрузки. Если есть случаи, нуждающиеся в рабочих регистрах после перезагрузки, Вы должны определить " SECONDARY_INPUT_RELOAD_CLASs " и, возможно, также " SECONDARY_OUTPUT_RELOAD_CLASs ", чтобы обнаружить их, и предоставить образцы " reload_inM " или " reload_outM ", чтобы обработать их. * См.: Классы Регистров::.

Ограничения на " moveM " должны позволять перемещать любой аппаратный регистр в любой другой аппаратный регистр при условии, что " HARD_REGNO_MODE_OK " позволеят тип M для обоих регистров, а " REGISTER_MOVE_COST ", примененный к их классам, возвращает значение 2.

Обязательно поддерживать команды " moveM " с плавающей точкой внутри и вне любых регистров, которые могут хранить значения с фиксированной точкой, потому что объединения и структуры (которые имеют типы " SImode " или " DImode ") могут быть в этих регистрах, и они могут иметь поля с плавающей точкой.

Может также иметься потребность поддерживать команды " moveM " с фиксированной точкой внутри и вне регистров с плавающей точкой. К сожалению, я забыл, почему это было так, и я не знаю, так ли это до сих пор. Если " HARD_REGNO_MODE_OK " отклоняет значения с фиксированной точкой в регистрах с плавающей точкой, то ограничения на команды " moveM " с фиксированной точкой должны быть предназначены, чтобы избежать попытки перезагрузки в регистр с плавающей точкой.

" reload_inM "

" reload_outM "

Подобно " movM ", но используется, когда рабочий регистр требуется, чтобы передвигать между операндом 0 и 1 операндом. Операнд 2 описывает рабочий регистр. См. обсуждение макрокоманды " SECONDARY_RELOAD_CLASs " в * См.: Классы Регистров:: ..

" movstrictm "

Подобно " movM " за исключением того, что, если операнд 0 - " subreg " с типом M регистра, чей естественный тип более широкий, " movstrictM " команда гарантированно не изменит регистра за исключением части, которая принадлежит типу M.

" load_multiple "

Загружает несколько последовательных ячеек памяти в последовательные регистры. Операнд 0 - первый из последовательных регистров, операнд 1 - первая ячейка памяти, а операнд 2 - константа: число последовательных регистров.

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

На некоторых машинах имеются ограничения на то, какая именно последовательность регистров может быть сохранена в память, типа ограничений на начальные или конечные номера регистров или на диапазон допустимых количеств. Для таких машин используйте " define_expand " (* См.: Расширитель определений::.) и делайте сбой образца, если ограничения не выполнены.

Записывайте сгенерированный insn как " parallel " с элементами, являющимися " set " одного регистра из соответствующей ячейки памяти (Вам могут также потребоваться " use " или " clobber " элементы). Используйте " match_parallel ", (* См.: RTL Шаблон::.) чтобы распознать insn. См. " a29k.md " и " rs6000.md " для примеров использования этого образца insn.

" store_multiple "

Подобно " load_multiple ", но сохраняет несколько последовательных регистров в последовательные ячейки памяти. Операнд 0 - первая из последовательных ячейки памяти, операнд 1 - первый регистр, а операнд 2 - константа: число последовательных регистров.

" addM3 "

Добавляет операнд 2 и 1 операнд, сохраняя результат в операнде 0. Все операнды должны иметь тип M. Это может использоваться даже на двухадресных машинах при помощи ограничений, требующих, чтобы операнды 1 и 0 находились в одном и том же месте.

" subM3 ", " mulM3 "

" divM3 ", " udivM3 ", " modM3 ", " umodM3 "

" sminM3 ", " smaxM3 ", " uminM3 ", " umaxM3 "

" andM3 ", " iorM3 ", " xorM3 "

Аналогично, но для других арифметических операций.

" mulhisi3 "

Перемножает операнды 1 и 2, которые имеют тип " HImode ", и сохраняет " SImode " результат в операнде 0.

" mulqihi3 ", " mulsidi3 "

Аналогичные команды умножения с расширением для другой ширины.

" umulqihi3 ", " umulhisi3 ", " umulsidi3 "

Аналогичные команды умножения с расширением, но для беззнакового умножения .

" mulM3_highpart "

Выполняет знаковое умножение операндов 1 и 2, которые имеют Режим M, и сохраняют старшую половину результата в операнд 0. Младшая половина результата теряется.

" umulM3_highpart "

Аналогичная команда для беззнакового умножения.

" divmodM4 "

Знаковое деление, дающее и частное, и остаток. Операнд 1 делится на операнд 2, частное сохраняется в операнде 0, а остаток сохраняется в операнде 3.

Для машин с командой, которая выдает и частное, и остаток, сделайте образец для " divmodM4 " но не делайте образцов для " divM3 " и " modM3 ". Это позволяет оптимизацию в относительно общем случае, когда и частное, и остаток вычисляются.

Если команда, которая вычисляет только частное или только остаток, существует и более эффективна, чем команда, которая вычисляет и то, и другое, пишите программу вывода " divmodM4 " для вызова " find_reg_note " и поиска примечания " REG_UNUSED " для частного или остатка и генерации соответствующей команды.

" udivmodM4 "

Аналогично, но делает деление без знака.

" ashlM3 "

Арифметический сдвиг операнда 1 влево на число битов, задаваемое операндом 2, с сохранением результата в операнде 0. Здесь M - тип операнда 0 и операнда 1; тип операнда 2 определяется образцом команды, и транслятор преобразует операнд к этому типу перед генерацией команды.

" ashrM3 ", " lshrM3 ", " rotlM3 ", " rotrM3 "

Другие команды сдвига и вращения , аналогичные " ashlM3 ".

" negM2 "

Получает число, противоположного операнду 1, и записывает результата в операнд 0.

" absM2 "

Записывает абсолютное значение операнда 1 в операнд 0.

" sqrtM2 "

Записывает квадратный корень из операнда 1 в операнд 0.

Встроенная функция C " sqrt " всегда использует тип, который соответствует типу данных C " double ".

" ffsM2 "

Записывает в операнд 0 единицу плюс номер самого младшего из установленных в 1 битов 1 операнда. Если операнд 1 - ноль, записывается ноль. M - тип операнда 0; операнда 1 тип определяется образцом команды, и транслятор преобразует операнд к этому типу перед генерацией команды.

Встроенная функция C " ffs " всегда использует тип, который соответствует типу данных C " int ".

" one_cmplM2 "

Записывает поразрядное дополнение операнда 1 в операнд 0.

" cmpM "

Сравнивает операнд 0 и операнд 1 и устанавливает условные коды. RTL образец должен выглядеть следующим образом:

           (set (cc0) (compare (match_operand:M 0 ...)
                               (Match_operand:M 1 ...)))
" tstM "

Сравнивает операнд 0 c нулем и устанавливает условные коды. RTL образец должен выглядеть следующим образом:

           (set (cc0) (match_operand:M 0 ...))
Образцы " tstM " не должны определяться для машин, которые не используют " (cc0) ", поскольку это помешало бы оптимизации, так как перестало бы быть понятным, какие операции " set " являются сравнениями. Вместо них должны использоваться образцы " cmpM ".

" movstrM "

Команда перемещения блока. Адреса исходной строки и строки-результата - первые два операнда, и они оба имеют тип " Pmode ". Число перемещаемых байтов - третий операнд, в типе M.

Четвертый операнд - известное общее выравнивание исходной строки и строки-результата, в форме " const_int " rtx. Таким образом, если транслятор знает, что обе строки выравниваются на границу слова, он может задавать значение 4 для этого операнда.

Этим образцам не требуется специальное рассмотрение возможности того, что источник и результат могут перекрываться.

" cmpstrM "

Команда сравнения блоков, с пятью операндами. Операнд 0 - результат; он имеет тип M. Остальные четыре операнда соответствуют операндам " movstrM ". Два указанных блока памяти сравниваются побайтно в лексикографическом порядке. Результатом команда является запись значения в операнд 0, знак которого указывает результат сравнения.

Вычисление длины строки, с тремя операндами. Операнд 0 - результат (типа M), операнд 1 - ссылка " mem " на первый символ строки, операнд 2 - символ для поиска (обычно ноль) и операнд 3 - константа, описывающая известное выравнивание начала строки.

" floatMN2 "

Преобразует целочисленный знаковый операнд 1 (допустимый для типа с фиксированной точкой M) к типу с плавающей точкой N и записывает результат в операнд 0 (который имеет тип N).

" floatunsMN2 "

Преобразует целочисленный беззнаковый операнд 1 (допустимый для типа с фиксированной точкой M) к типу с плавающей точкой N и записывает результат в операнд 0 (который имеет тип N).

" fixMN2 "

Преобразует операнд 1 (допустимый для типа с плавающей точкой M) к типу с фиксированной точкой N как число со знаком и записывает результат в операнд 0 (который имеет тип N). Результат этой команды определен только тогда, когда значение операнда 1 - целое число.

" fixunsMN2 "

Преобразует операнд 1 (допустимый для типа с плавающей точкой M) к типу с фиксированной точкой N как число без знака и записывает результат в операнд 0 (который имеет тип N). Результат этой команды определен только тогда, когда значение операнда 1 - целое число.

" ffruncM2 "

Преобразует операнд 1 (допустимый для типа с плавающей точкой M) к целому числу, по-прежнему представляемому в типе с плавающей точкой M, и записывает результат в операнд 0 (который имеет тип M).

" fix_truncMN2 "

Подобно " fixMN2 ", но преобразует значение с плавающей точкой типа M к целому числу.

" fix_truncMN2 "

Подобно " fixunsMN2 ", но преобразует значение с плавающей точкой типа M к целому числу.

" truncMN "

Усекает операнд 1 (допустимый для типа M) к типу N и записывает в операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной точкой или оба с плавающей точкой.

" extendMN "

Расширяет знаком операнд 1 (допустимый для типа M) к типу N и записывает в операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной точкой или оба с плавающей точкой.

" zero_extendMN "

Расширяет нулем операнд 1 (допустимый для типа M) к типу N и записывает в операнд 0 (который имеет тип N). Режимы должны быть оба с фиксированной точкой.

" Экстелевидение "

Извлечекает поле битов из операнда 1 (регистра или ячейки памяти), где операнд 2 определяет ширину в битах, а операнд 3 - начальный бит, и записывает его в операнд 0. Операнд 0 должен иметь тип " word_mode ". Операнд 1 может иметь тип " byte_mode " или " word_mode "; часто " word_mode " позволяется только для регистров. Операнды 2 и 3 должны быть допустимы для " word_mode ".

Проход генерации RTL генерирует эту команду только с константами в качестве операндов 2 и 3.

Значение битового поля расширяется знаком до полного целочисленного слова перед записью в операнд 0.

" extzv "

Подобно " extv ", за исключением того, что значение битового поля расширяется нулем.

" insv "

Записывает операнд 3 (который должен быть допустимым для " word_mode ") в битовое поле в операнде 0, где операнд 1 определяет ширину в битах, а операнд 2 - начальный бит. Операнд 0 может иметь тип " byte_mode " или " word_mode "; часто " word_mode " позволяется только для регистров. Операнды, 1 и 2 должны быть допустимыми для " word_mode ".

Проход генерации RTL генерирует эту команду только с константами в качестве операндов 1 и 2.

" movMODEcc "

Перемещает операнд 2 или операнд 3 в операнд 0 согласно сравнению в операнде 1. Если сравнение истинно, операнд 2 перемещается в операнд 0, иначе перемещается операнд 3.

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

Если машина не имеет условных команд перемещения, не определяйте эти образцы.

" sCOND "

Записывает ноль или отличное от нуля число в операнд согласно коду условия. Записываемое значение отлично от нуля, если условие COND истинно. COND - имя кода выражения операции сравнения, такого как " eq ", " lt " или " leu ".

Вы указываете тип, который операнд должен иметь, когда Вы пишете выражение " match_operand ". Транслятор автоматически видит, какой тип Вы использовали и поддерживает операнд этого типа.

Значение, сохраненное для истинного условия, должно иметь 1 в младшем бите или должно быть отрицательно. Иначе команда не подходит и Вы должны исключить ее из машинного описания. Вы описываете транслятору точно, какое значение записывается, определяя макрокоманда " STORE_FLAG_VALUE " (* См.: Разное::.). Если описание не найдено, что может использоваться для всех образцов " sCOND ", Вы должны исключить эти операции из машинного описания.

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

Если эти операции исключены, транслятор будет обычно генерировать код, который копирует константу 1 в адресат и делает условный переход вокруг присваивания нуля адресату. Если этот код более эффективен, чем потенциальные команды, используемые для образца " sCOND ", сопровождаемые теми, которые требуются, чтобы преобразовать результат в 1 или ноль в " SImode ", Вы должны исключить " sCOND " операции из машинного описания.

" bCOND "

Команда условного перехода. Операнд 0 - " label_ref ", ссылающийся на метку, на которую делается переход. Переход происходит, если коды условия удовлетворяют условию COND.

Некоторые машины не следуют модели, принятой здесь, в которой команда сравнения сопровождается командой условного перехода. В этом случае образцы " cmpM " (и " tstM ") должны просто сохранять куда-нибудь операнды и генерировать все необходимые insns в " define_expand " (* См.: Расширение определений::.) для операций условного перехода(выполняет перехода). Всем обращениям к расширению образцов " bCOND " должны непосредственно предшествовать обращения к расширению образцов " cmpM " или " tstM ".

Машины, которые используют псевдорегистр для значения кода условия, или где тип, используемый для сравнения, зависит от проверяемого условия, должен также использовать вышеупомянутый механизм. * См.: Образцы Переходов::

Вышесказанное применимо также к образцам " movMODEcc " и " sCOND ".

" call "

Команда вызова подпрограммы, не возвращающая значения. Операнд 0 - вызываемая функция; операнд 1 - число байтов параметров, помещенных в стек (в типе " SImode ", если это не " const_int "); Операнд 2 - число регистров, используемых как операнды.

На большинстве машин операнд 2 фактически не сохранен в образце RTL. Это делается ради некоторых RISC машин, которые должны поместить эту информацию в код ассемблера; они могут помещать ее в RTL вместо операнда 1.

Операнд 0 должен быть " mem " RTX, чей адрес - адрес функции. Обратите внимание, однако, что этот адрес может быть " symbol_ref " выражением, даже если он не был бы законным адресом памяти на целевой машине. Если это также недопустимый параметр для команды вызова, образцом для этой операции должен быть " define_expand " (* См.: Расширение определений::.), который помещает адрес в регистр и использует этот регистр в команде вызова.

" call_value "

Команда вызова подпрограммы, возвращающая значение. Операнд 0 - аппаратный регистр, в котором значение возвращается. Имеется еще три операнда, такие же, как три операнда команды " call " (но с номерами, увеличенными на один).

Подпрограммы, которые возвращают объекты ` BLKmode ', используют " call " insn.

" call_pop ", " call_value_pop "

Аналогичны " call " и " call_value ", за исключением того, что используются, если определены и если " RETURN_POPS_ARGS " отлично от нуля. Они должны сгенерировать " parallel ", который содержит и обращение к функции, и " set ", чтобы указать корректировку, делаемую для указателя кадра.

Для машин, где " RETURN_POPS_ARGS " может быть отлично от нуля, использование этих образцов увеличивает число функций, для которых указатель кадра может быть удален, если это желательно.

" untyped_call "

Команда вызова подпрограммы, возвращающая значение любого типа. Операнд 0 - вызываемая функция; операнд 1 - ячейка памяти, где должен быть сохранен результат вызова функции; операнд 2 - выражение ` parallel ', где каждый элемент - выражение " set ", которое указывает запись возвращаемого функцией значения в блок результата.

Этот образец команды должен быть определен, чтобы поддерживать " __ builtin_apply " на машинах, где для вызова подпрограммы с произвольными параметрами или сохранения возвращенного значения необходимы специальные команды. Этот образец команды требуется на машинах, имеющих несколько регистров, которые могут содержать возвращаемое значение (то есть " FUNCTION_VALUE_REGNO_P " является истиной для более, чем одного регистра).

" return "

Команда возврата из подпрограммы. Это имя образца команды должно быть определено только, если одиночная команда может выполнить всю работу по возврату из функции.

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

Для таких машин, условие, определенное в этом образце должно быть истинным только, когда " reload_completed " отлично от нуля и эпилог функции является одиночной командой. Для машин с окнами регистров подпрограмма " leaf_function_p " может использоваться для определения, требуется ли временное сохранение в окне регистра.

Машины, которые имеют условные команды возврата, должны определить образцы, подобные следующему:

           (define_insn ""
             [(set (PC)
                   (if_then_else (match_operator
                                    0 "comparison_operator"
                                    [(cc0) (const_int 0)])
                                 (return)
                                 (pc)))]
             "CONDITION"
             "...")
Где CONDITION обычно - то же самое условие, что и указанное в поименованном образце " return ".

" untyped_return "

Команда возврата из подпрограммы без контроля типов . Этот образец команды должен быть определен для поддержки " __ builtin_return " на машинах, где необходимы специальные команды, чтобы возвратить значение любого типа.

Операнд 0 - ячейка памяти, куда записывается результат вызова функции с " __ builtin_apply "; операнд 1 - выражение ` parallel ', где каждый элемент - выражение ` set ', указывающее восстановление значения, возвращаемого функцией из блока результата.

" nop "

Пустая команда. Это имя образца команды должно быть всегда определено, чтобы вывести пустую команду в коде ассемблера. " (const_int 0) " будет делать это как образец RTL.

" indirect_jump "

Команда для перехода к адресу, который является нулевым операндом. Это имя образца обязательно на всех машинах.

" casesi "

Команда для перехода через таблицу, включая проверку границ. Эта команда имеет пять операндов:

  1. Индекс для посылки, который имеет тип " SImode ".
  2. Нижняя граница индексов в таблице, целочисленная константа.
  3. Общий диапазон индексов в таблице - самый большой индекс минус самый маленький (включая оба конца).
  4. Метка, которая непосредственно предшествует таблице.
  5. Метка, на которую делается переход, если индекс имеет значение вне границ. (Если макрокоманда с машинным описанием " CASE_DROPS_THROUGH " определена, то такой индекс влечет переход к коду после таблицы переходов вместо перехода к этой метке. В таком случае эта метка в действительности не используется командой " casesi ", но она всегда задается как операнд.)
Таблица - это " addr_vec " или " addr_diff_vec " внутри " jump_insn ". Число элементов в таблице - один плюс разность между верхней и нижней границами.

" tablejump "

Команда для перехода к переменному адресу. Это - низкоуровневая возможность, которая может использоваться, чтобы выполнить таблицу переходов, когда нет образца " casesi ".

Этому образцу требуется два операнда: адрес или смещение, и метка, которая должна непосредственно предшествовать таблице перехода. Если макрокоманда " CASE_VECTOR_PC_RELATIVE " определена, то первый операнд является смещением, которое рассчитывается из адреса таблицы; иначе, это - абсолютный адрес перехода. В любом случае первый операнд имеет тип " Pmode ".

" tablejump " insn - всегда последний insn перед таблицей перехода, которую он использует. Код ассемблера обычно не имеет потребности в использовании второго операнда, но Вы должны соединить их в образце RTL так, что оптимизатор перехода не будет удалять таблицу как недостижимый код.

" save_stack_block "

" save_stack_function "

" save_stack_nonlocal "

" restore_stack_block "

" restore_stack_function "

" Restore_stack_nonlocal "

Большинство машин сохраняет и восстанавливает указатель стека, копируя его в или из объекта типа " Pmode ". Не определяйте эти образцы на таких машинах.

Некоторые машины требуют специальной обработки для сохранения и восстановления указателя стека. На этих машинах определяйте образцы соответственно нестандартным случаям, используя " define_expand " (* См.: Расширение Определений::.), который производит требуемый insns. Вот три типа сохранения и восстановления:

  1. " save_stack_block " сохраняет указатель стека в начале блока, который распределяет объект переменного размера, а " restore_stack_block " восстанавливает указатель стека, когда блок покидается.
  2. " save_stack_function " и " restore_stack_function " делают аналогичную работу для самого внешнего блока функции и используются, когда функция распределяет объекты переменного размера или вызывает " alloca ". Только эпилог использует восстановленный указатель стека, позволяя более простую последовательность сохранения и восстанавления на некоторых машинах.
  3. " save_stack_nonlocal " используется в функциях, которые содержат метки для перехода к вложенными функциями. Он сохраняет указатель стека таким способом, что внутренняя функция может использовать " restore_stack_nonlocal ", чтобы восстановить указатель стека. Транслятор генерирует код, чтобы восстановить регистры указателей кадра и параметров, но некоторые машины требуют сохранения и восстановления дополнительных данных типа информации окна регистра или обратных цепочек стека. Поместите в эти образцы insns для сохранения и восстановления любых таких данных.
При сохранении указателя стека операнд 0 - область сохранения и операнд 1 - указатель стека. Режим, используемый для распределения области сохранения - тип операнда 0. Вы должны указать целый тип или " VOIDmode ", если область сохранения не нужна в данном случае (потому что сохранение не требуется или потому что может использоваться сохранение в машинно-специфическую область сохранения). Операнд 0 - указатель стека, а операнд 1 - область сохранения для операций восстановления. Если " save_stack_block " определен, операнд 0 не должен быть " VOIDmode ", так как такие сохранения могут произвольно вкладываться.

Область сохранения - " mem ", который является константным смещении от " virtual_stack_vars_rtx ", когда указатель стека сохраняется для использования нелокальных goto и " reg " в других двух случаях.

" allocate_stack "

Вычитает (или добавляет, если " STACK_GROWS_DOWNWARD " не определено) операнд 0 из указателя стека, чтобы создать пространство для динамически распределяемых данных.

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

5.8 Когда Порядок Образцов Играет Роль

Иногда insn может соответствовать более чем одному образцу команды. Тогда используется образец, который появляется первым в машинном описании. Следовательно, более специфические образцы (образцы, которые будут соответствовать меньшее количество insns) и более быстрые команды (которые производят лучший код, когда имеет место соответствие им) должны обычно идти сначала в описании.

В некоторых случаях эффект упорядочивания образцов может использоваться, чтобы скрыть образец, когда он не является допустимым. Например, 68000 имеет команду для преобразования слова к плавающей точке и другую для преобразования байта к плавающей точке. Команда, преобразующая целое число к плавающей точке может соответствовать любой из них. Мы помещаем образец преобразования слова сначала, чтобы удостовериться, что он будет использоваться скорее, чем другой. (Иначе большое целое число могло бы быть сгенерировано как несколько одиночных байтов, что не работало бы.) Вместо использования такого упорядочивания образцов можно было бы сделать образец для convert-a-byte достаточно "умный", чтобы правильно обрабатывать любую целочисленную константу.

5.9 Взаимозависимость Образцов

Каждое машинное описание должно иметь поименованный образец для каждого из имен условных переходов " bCOND ". Шаблон распознавания должен всегда иметь форму

      (set (PC)
           (if_then_else (COND (cc0) (const_int 0))
                         (label_ref (match_operand 0 "" ""))
                         (pc)))
Кроме того, каждое машинное описание должно иметь безымянный образец для каждой из возможных обратных ветвлений. Их шаблоны выглядят примерно так:

      (set (PC)
           (if_then_else (COND (cc0) (const_int 0))
                         (pc)
                         (label_ref (match_operand 0 "" ""))))
Они необходимы, потому что оптимизация перехода может превращать прямое ветвление в обратное.

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

      (define_insn ""
        [(set (PC)
              (if_then_else (match_operator 0 "comparison_operator"
                                            [(cc0) (const_int 0)])
                            (pc)
                            (Label_ref (match_operand 1 "" ""))))]
        "CONDITION"
        "...")
В некоторых случаях машины поддерживают команды, идентичные во всем, кроме машинного типа одного или более операндов. Например, могут иметься команды "sign-extend halfword" и "sign-extend byte" с такими образцами:

      (set (match_operand:SI 0 ...)
           (extend:SI (match_operand:HI 1 ...)))
 
      (set (match_operand:SI 0 ...)
           (extend:SI (match_operand:QI 1 ...)))
Целые константы не определяют машинный тип, так что команда расширения константы может соответствовать любому образцу. Образцом, которому она будет фактически поставлена в соответствие, будет тот, который появляется первым в файле. Для правильных результатов это должен быть образец для самого широкого из возможных типов (здесь - " HImode "). Если бы образец соответствовал команде " QImode ", результаты могли бы быть неправильными, если константа фактически не подходит для этого типа.

Такие команды расширения констант редко генерируются, потому что они оптимизируются, но они могут иногда встречаться при неоптимизированной трансляции.

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

5.10 Определение Образцов Команд Перехода

Для большинства машин GNU CC принимает, что машина имеет условный код. Сравнение insn устанавливает условный код, записывая результат как знакового, так и беззнакового сравнения в заданные операнды. Отдельный insn ветвления проверяет код условия и производит или не производит переход согласно его значению. Среди insns ветвления различают знаковые и беззнаковые. Много часто встречающихся машин, типа Vax, 68000 и 32000, работают этим путем.

Некоторые машины имеют различные команды для знакового и беззнакового сравнения и только один набор команд условного перехода. Самый простой способ обработки этих машин состоит в том, чтобым обрабатывать их точно так же, как и другие, до заключительной стадии, где записывается ассемблерный код. В это время, когда выводится код для команды сравнения, нужно заглянуть вперед в следующий переход, использующий " next_cc0_user (insn) ". (Переменная " insn " ссылается на insn, выводящийся в данный момент, в коде записи вывода коде в образца команды.) Если RTL говорит, что это переход без знака, нужно выводить беззнаковое сравнение; иначе нужно выводить знаковое сравнение. Когда ветвь сама является выводом, Вы можете обрабатывать знаковые и беззнаковые переходы одинаково.

Причина, по которой так можно делать, заключается в том, что GNU CC всегда генерирует пару последовательных RTL insns, возможно отделяемый " note " insns, один, чтобы установить код условия, и один, чтобы проверить его, и хранит эту пару до конца без изменений.

Чтобы так поступить, Вы должны определить макрокоманду машинного описания " NOTICE_UPDATE_CC ", чтобы делать " CC_STATUS_INIT "; другими словами, нет излишних команд сравнения.

Некоторые машины имеют команды одновременного сравнения и перехода и не имеют условного кода. Подобная методика работает для них. Когда наступает время, чтобы "вывести" команду сравнения, запишите ее операнды в двух статических переменных. Когда выводится следующая команда одновременного сравнения и перехода, фактически выводится команды одновременного сравнения и перехода, которая использует записанные операнды.

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

На многих RISC машинах большинство команд не воздействует на код условия и там может даже не быть отдельного регистра кода условия. На этих машинах ограничение, что определение и использование кода условия должно производиться смежными insns, не необходимо и может помешать важным оптимизациям. Например, на IBM RS/6000, имеется задержка для принимаемых ветвей, если регистр кода условия не установлен на три команды раньше условного перехода. Планировщик команд не может выполнять эту оптимизацию, если не разрешается разделите определение и использование регистра кода условия.

На этих машинах не используйте " (cc0) ", а используйте взамен регистр для представления кода условия. Если в машине имеется определенный регистр кода условия, используйте аппаратный регистр. Если код условия или результат сравнения может быть помещен в любой общий регистр, или если имеется несколько регистров условия, используйте псевдорегистр.

На некоторых машинах тип сгенерированной команды перехода может зависеть от способа, которым код условия был произведен; например, на 68k и Sparc установка кода условия непосредственно из команд сложения или вычитания не очищает бит переполнения, в отличие от команды тестирования, так что для некоторых ветвлений должны использоваться различные команды перехода. Для машин, которые используют " (cc0) ", установка и использование кода условия должны быть смежными (разделяясь только " note " insns), позволяя использование флагов в " cc_status ". (* См.: Код Условия::.) Кроме того, сравнение и переход insns может быть найдены друг из друга, при помощи функции " prev_cc0_setter " и " next_cc0_user ".

Однако, это не так на машинах, которые не используют " (cc0) ". На этих машинах нельзя сделать никаких предположений о смежности insns сравнения и перехода, и вышеупомянутые методы не могут использоваться. Вместо этого мы используем машинный тип регистра кода условия, чтобы записать различные форматы регистра кода условия.

Регистры, используемые, чтобы сохранить значение кода условия, должны иметь тип, который находится в классе " MODE_CC ". Обычно это будет " CCmode ". Если требуются дополнительные типы (как для вышеупомянутого примера сложения на Sparc), определите макрокоманду " EXTRA_CC_MODES ", чтобы внести в список дополнительные требуемые типы (* См.: Код Условия::.). Также определите " EXTRA_CC_NAMES ", чтобы внести в список имена этих типов и " SELECT_CC_MODE ", чтобы выбрать тип операнда сравнения.

Если в течение генерации RTL известно, что потребуются различные типы, (например, если машина имеет отдельные команды для знакового и беззнакового сравнений, подобно большинству процессоров IBM), они могут быть определены в это время.

Если случаи, которые требуют различных типов, получаются при комбинировании команд, макрокоманда " SELECT_CC_MODE " определяет, какой машинный тип должен использоваться для результата сравнения. Образцы должны быть написаны с использованием этого типа. Для поддержки случай случая сложения на Sparc, обсуждавшегося выше, мы имеем образец

      (define_insn ""
        [(set (reg: CC_NOOV 0)
              (compare: CC_NOOV
                (plus: SI (match_operand:SI 0 "register_operand" "%r")
                         (Match_operand:SI 1 "arith_operand" "rI"))
                (const_int 0)))]
        ""
        "...")
Макрокоманда " SELECT_CC_MODE " на Sparc возвращает " CC_NOOVmode " для сравнения, чей параметр - " plus ".

5.11 Канонизация Команд

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

В дополнение к алгебраическим упрощениям, выполняются следующие канонизации:

  • Для операторов сравнения и коммутации, константа всегда делается вторым операндом. Если машина поддерживает константу только как второй операнд, то необходима поддержка только образцов с константой в качестве второго операнда. Для этих операторов, если один из операндов - выражение " neg ", " not ", " mult ", " plus ", или "minus ", то он будет первым операндом.
  • Для оператора " compare " константа - всегда второй операнд на машинах, где используется " cc0 " (* См.: Образцы Переходов::.). На других машинах встречаются редкие случаи, когда транслятор хочет создать " compare " с константой в качестве первого операнда. Однако, эти случаи не достаточно общие для того, чтобы делать образец, в котором константа является первым операндом, если реально машина не имеет такой команды. Операнд " neg ", " not ", " mult ", " plus ", или " minus " делается первым операндом при тех же условиях, что и выше.
  • " (minus X (const_int N)) " преобразуется в " (plus X (const_int -N)) ".
  • Внутри вычислений адреса (то есть внутри " mem ") левый сдвиг преобразуется в соответствующее умножение на степень двух. Чтобы переместить поразрядное отрицание внутри поразрядных операций логического "И" или логического "ИЛИ", используется закон Моргана. Если это приводит к одному операнду, являющемуся выражением " not ", это будет первый. Машина, имеющая команду, которая выполняет поразрядное логическое "И" одного операнда с поразрядным отрицанием другого, должна определять образец для этой команды так:
               (define_insn ""
                 [(set (match_operand:M 0 ...)
                       (and:M (not:M (match_operand:M 1 ...))
                                    (match_operand:M 2 ...)))]
                 "..."
                 "...")
    
    Аналогично, образец для "NAND" команды должен записываться так:
               (define_insn ""
                 [(set (match_operand:M 0 ...)
                       (ior: M (not:M (match_operand:M 1 ...))
                                    (not:M (match_operand:M 2 ...))))]
                 "..."
                 "...")
    
    В обоих случаях необязательно включать образцы для многих логически эквивалентных выражений RTL.
  • Единственые возможные выражения RTL, включающие как поразрядное исключающее "ИЛИ", так и поразрядное отрицание - это " (xor:M X Y) " и " (not:M (xor:M X Y)) ".
  • Сумма трех элементов, один из которых - константа, будет появляться только в форме
               (plus:M (plus:M X Y) CONSTANT)
    
  • На машинах, которые не используют " cc0 ", " (compare X (const_int 0)) " будет преобразовано в X.
  • Сравнения на равенство группы битов (обычно одиночного бита) с нулем будет записаны, используя " zero_extract " скорее, чем эквивалент операций " and " или " sign_extract ".

5.12 Машинно - специфические локальные оптимизации

В дополнение к образцам команд " md " файл может содержать определения машинно - специфических peephole оптимизаций. Peephole оптимизация - это оптимизация " просмотром вперед ", т.е. поиск команд, которые можно объединить с данной для ускорения их выполнения.

Комбинатор не обращает внимание на некоторые peephole оптимизации, когда поток данных в программе не предполагает, что он попробует сделать их. Например, иногда два последовательного insns, связанные по цели, могут быть oбъединены, даже если кажется, что второй не использует регистр, вычисленный в первом. Машинно - специфический peephole оптимизатор может обнаружить такие возможности.

Определение выглядит примерно так:

      (define_peephole
        [INSN-PATTERN-1
         INSN-PATTERN-2
         ...]
        "CONDITION"
        "TEMPLATE"
        " OPTIONAL INSN-ATTRIBUTES ")
Последний строковый операнд может быть опущен, если Вы не используете никакой машинно-специфической информации в этом машинном описании. Если он имеется, то он должен подчиняться тем же правилам,что и в " define_insn ".

В этом скелете INSN-PATTERN-1 и так далее - образцы, которые ставятся в соответствие последовательным insns. Оптимизация применяется к последовательности insns, когда INSN-PATTERN-1 соответствует первому, INSN-PATTERN-2 соответствует следующему, и так далее.

Каждый из insns, согласованный по peephole должен также соответствовать " define_insn ". Peepholes проверяются только в последней стадии, непосредственно перед генерацией кода, и только опционально. Следовательно, всякий insn, который соответствовал бы peephole, но не соответствовал бы никакому " define_insn ", вызовет аварию в генерации кода при неоптимизированной трансляции или на различных стадиях оптимизации.

Операнды insns согласованы с " match_operands ", " match_operator " и " match_dup ", как обычно. Необычно то, что количество операндов относится ко всем образцам insn в определении. Так, Вы можете проверять идентичность операндов в двух insns, используя " match_operand " в одном insn и " match_dup " в другом.

Ограничения на операнды, используемые в образцах " match_operand " не имеют прямого эффекта на применимости peephole, но они будет проверены позже, так что удостоверьтесь, что ваши ограничения достаточно общие, чтобы применять их всякий раз для peephole. Если peephole согласовано, но ограничения не выполнены, это вызовет аварию трансляции.

Безопасно опустить ограничения во всех операндах peephole; или Вы можете писать ограничения, которые служат, как двойная проверка на критерии, предварительно проверенные.

Как только последовательность insns соответствует образцам, CONDITION проверяется. Это - выражение C, которое делает окончательное решение, выполнять ли оптимизацию (она выполняется, если выражение отлично от нуля). Если CONDITION опущено (другими словами, строка пуста), то оптимизация применяется к каждой последовательности insns, которая соответствует образцам.

Определенные peephole оптимизации применяются после окончания распределения регистров. Следовательно, peephole определение может проверять, какие операнды заканчиваются в каких видах регистров, только просматривая операнды.

Способ обратиться к операндам CONDITION состоит в том, чтобы писать " operands[I] " для операнда номер I (как согласованному с " (match_operand I...) "). Используйте переменную " insn ", чтобы обратиться к последнему из согласовываемых insns; используйте " prev_active_insn ", чтобы найти предшествующие insns.

При оптимизации вычислений с промежуточными результатами Вы можете использовать CONDITION для создания соответствия только тогда, когда промежуточные результаты не используются нигде в другом месте. Используйте выражение C " dead_or_set_p (INSN, OP) ", где INSN является insn, в котором значение, как Вы считаете, используется в последний раз (из значения " insn ", вместе с использованием " prev_nonnote_insn "), и OP - промежуточное значение (из " operands[I] ").

Применение средств оптимизации означает замену последовательности insns на один новый insn. TEMPLATE управляет окончательным выводом ассемблерного кода для этого объединенного insn. Он работает точно так же, как шаблон " define_insn ". Количества операндов в этом шаблоне - те же, что использовались при установлении соответствия для первоначальной последовательности insns.

Результат определяемого peephole оптимизатора не обязан соответствовать какому-то из образцов insn в машинном описании; он даже не имеет возможности соответствовать им. Определение peephole оптимизатора само служит как образец insn для управления выводом insn.

Определяемые peephole оптимизаторы выполняются во время вывода кода ассемблера, так что insns, который они производят, никогда не объединяются и не переставляются никаким образом.

Вот пример, взятый из машинного описания 68000:

      (define_peephole
        [(set (reg:SI 15) (plus:SI (reg:SI 15) (const_int 4)))
         (set (match_operand:DF 0 "register_operand" "=f")
              (match_operand:DF 1 "register_operand" "ad"))]
        "FP_REG_P (operands[0]) && ! FP_REG_P (operands[1])"
        "*
      {
        rtx xoperands[2];
        xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
      #ifdef MOTOROLA
        output_asm_insn (\"move.l %1,(sp)\", xoperands);
        output_asm_insn (\"move.l %1,-(sp)\", operands);
        return \"fmove.d (sp)+,%0\";
      #else
        output_asm_insn (\"movel %1,sp@\", xoperands);
        output_asm_insn (\"movel %1,<url url="mailto:sp@-" name="sp@-">\", operands);
        return \"fmoved sp@+,%0\";
      #endif
      }
      ")

Эффект этой оптимизации заключается в изменении

      jbsr _foobar
      addql *4,sp
      movel d1,<url url="mailto:sp@-" name="sp@-">
      movel d0,<url url="mailto:sp@-" name="sp@-">
      fmoved sp@+, fp0
на

      jbsr _foobar
      movel d1,sp@
      movel d0,<url url="mailto:sp@-" name="sp@-">
      fmoved sp@+,fp0
INSN-PATTERN-1 и так далее выглядят *почти* как второй операнд " define_insn ". Имеется одно важное различие: второй операнд " define_insn " состоит из одного или нескольких RTX, заключенных в квадратные скобки. Обычно, имеется только один: затем то же самое действие может быть записано как элемент " define_peephole ". Но когда в " define_insn " несколько действий, они неявно включены в " parallel ". Поэтому Вы должны явно писать " parallel " и квадратные скобки внутри него в " define_peephole ". Таким образом, если образец insn выглядит так:
      (define_insn "divmodsi4"
        [(set (match_operand:SI 0 "general_operand" "=d")
              (div:SI (match_operand:SI 1 "general_operand" "0")
                      (match_operand:SI 2 "general_operand" "dmsK")))
         (set (match_operand:SI 3 "general_operand" "=d")
              (mod:SI (match_dup 1) (match_dup 2)))]
        "TARGET_68020"
        "divsl%.l %2,%3:%0")
то способ упомянуть этот insn в peephole таков:

      (define_peephole
        [...
         (parallel
          [(set (match_operand:SI 0 "general_operand" "=d")
                (div:SI (match_operand:SI 1 "general_operand" "0")
                        (match_operand:SI 2 "general_operand" "dmsK")))
           (set (match_operand:SI 3 "general_operand" "=d")
                (mod:SI (match_dup 1) (match_dup 2)))])
         ...]
        ...)

5.13 Определенные RTL последовательности для генерации кода

На некоторых целевых машинах отдельные стандартные имена образцов для RTL поколения не могут быть обработаны одиночным insn, но их можно представить последовательностью insns. Для этих целевых машин, Вы можете описать 'define_expand', чтобы определить, как генерировать последовательность RTL.

'define_expand' является RTL-выражением, которое работает почти подобно 'define_insn', но, в отличие от последнего, 'define_expand' используется только для RTL поколения и он может произвести больше чем один RTL-insn.

RTX 'define_expand' имеет четыре операнда:

  • Имя. Каждый 'define_expand' должен иметь имя, так как обратится к нему можно только через имя.
  • RTL шаблон. Точно так же как для RTL шаблона для 'define_peephole', это массив выражений RTL, каждый из которых - один insn.
  • Условие - строка, содержащая выражение на С. Это выражение используется, что бы выразить, как доступность этого образца зависит от подклассов целевой машины, выбираемых с помощью командно-строчных опций, когда GNU CC запущен. Это похоже на условие 'define_insn', которое имеет стандартное имя. Таким образом, условие (если присутствует) может не зависеть от данных в согласовываемом insn, но только от флагов типа в целевой машине. Транслятор должен проверить эти условия во время инициализации, что бы узнать какие именно названные инструкции доступны в особенном запуске (particular run).
  • Операторы подготовки - строка, содержащая ноль или больше С-утверждений, которые должны быть выполнены до того, как из RTL-шаблона будет сгенерирован RTL-код. Обычно эти операторы подготавливают временные регистры для использования в виде внутренних операндов, но они могут также непосредственно генерировать RTL insns, вызывая подпрограммы типа 'emit_insn', и т.д. Любой такой insns предшествует insns, полученным из RTL шаблона.
Каждому RTL insn, сгенерированному `define_expand' должен соответствовать какой-нибудь `define_insn' в описании машины. Иначе транслятор разрушится при попытке сгенерировать соответствующий код, или при попытке его оптимизации.

Так же как и операторы управления RTL insns, RTL шаблон описывает операнды, которые должны быть определены, при использовании образца. В частности, это дает предикат для каждого операнда.

Правильный операнд, который должен быть указан при генерации RTL с образца, должен быть описан с помощью 'mathch_operand' в свое первое появления в RTL шаблоне. Это вводит информацию о предикате операнда в таблицы, в которые записываются такие вещи. GNU CC использует эту информацию для предзагрузки операнда в регистр, если это требуется, что бы сделать корректный RTL код. Если на операнд ссылаются более одного раза, то последующие ссылки должны использовать 'mathch_dup'.

RTL-шаблон может также ссылаться на внутренние 'операнды', которые являются временными регистрами или метками, используемыми только в последовательности, сгенерированной 'define_expand'. Внутренние операнды в RTL шаблоне заменяются на 'match_dup', но никогда не на 'match_operand'. Значение внутренних операндов, когда требуется использование этого образца, не передаются транслятором как аргументы. Вместо этого они вычисляются в образце, в операторах подготовки. Эти операторы вычисляют значения и записывают их в соответствующие элементы 'операндов' так, чтобы 'match_dup' мог их найти.

Имеются две специальных макрокоманды, определенные для использования в операторах подготовки: `DONE' and `FAIL'. Если поставить после них ';' то можно использовать их как операторы.

`DONE'

Используйте макрокоманду `DONE', чтобы закончить для образца генерацию RTL. Единственными RTL insn, полученными из образца в этом случае будут те, которые уже сгенерированы явными обращениями к 'emit_insn' в операторах подготовки; RTL шаблон не будет сгенерирован.

`FAIL'

Заставляет образец обломиться. Когда образец обламывается, это означает, что образец не был по настоящему доступен. Тогда подпрограммы вызова в трансляторе пробует другие способы для генерации кода, используя другие образцы.

Отказ в настоящее время поддерживается только для бинарных (сложение, умножение, смещение, и т.д.) и битовых (`extv', `extzv', и `insv') операций .

Далее идет пример определения левого сдвига для чипа SPUR:

      (define_expand 'ashlsi3'
        [(set (match_operand:SI 0 'register_operand' '')
              (ashift:SI
 
      (match_operand:SI 1 'register_operand' '')
                (match_operand:SI 2 'nonmemory_operand' '')))]
        ''
        '
 
      {
        if (GET_CODE (operands[2]) != CONST_INT
            || (unsigned) INTVAL (operands[2]) > 3)
          FAIL;
      }')
Этот пример использует 'define_expand' так, что он генерирует RTL insn для сдвига, когда величина сдвига от 0 до 3, и обламывается в противном случае. Когда он обламывается транслятор пытается использовать другую стратегию, используя различные образцы (такие, как library call).

Если транслятор умел бы обрабатывать нетривиальные строки условий в образцах с именами, то было бы возможно использовать в этом случае 'define_insn'. Вот другой случай (нуль-расширение на 68000), который более полно использует мощь 'define_expand':

      (define_expand 'zero_extendhisi2'
        [(set (match_operand:SI 0 'general_operand' '')
              (const_int 0))
         (set (strict_low_part
                (subreg:HI
                  (match_dup 0)
                  0))
              (match_operand:HI 1 'general_operand' ''))]
        ''
        'operands[1] = make_safe_from (operands[1], operands[0]);')
Здесь сгенерированы два RTL insn - один что бы очистить весь выходной операнд, а другой для того, что бы копировать входной операнд в его нижнею половину. Эта последовательность некорректна если входной операнд ссылается на выходной операнд (на его старое значение), поэтому операторы подготовки проверяют, что это не так. Функция 'make_safe_from' копирует операнд 'operands[1]' во временный регистр если он обращается к 'operands[0]'. Это делается с помощью генерации другого RTL insn.

Наконец, третий пример демонстрирует использование внутреннего операнда Ноль-расширение на SPUR чипах осуществляется с помощью применения 'and' к результату и маски половины слова. Но эта маска не может быть представлена с помощью 'const_int', потому что значение константы слишком велико для его корректного представления на этой машине. Поэтому она должна быть скопирована в регистр с помощью 'force_reg' и тогда этот регистр используется в операции 'and'.

      (define_expand 'zero_extendhisi2'
        [(set (match_operand:SI 0 'register_operand' '')
              (and:SI (subreg:SI
                        (match_operand:HI 1 'register_operand' '')
                        0)
                      (match_dup 2)))]
        ''
        'operands[2]
           = force_reg (SImode, gen_rtx (CONST_INT,
                                         VOIDmode, 65535)); ')
*Замечание: Если 'define_expand' используется для того, что бы обслужить стандартную бинарную или унарную арифметическую операцию, то последний insn в сгенерированной последовательности не должен быть 'code_label',`barrier' или `note'. Он должен быть `insn', `jump_insn' или `call_insn'. Если Вам не нужен настоящий insn в конце, то сгенерируйте insn, который копирует результат операций сам в себя. Такой insn не генерирует кода, но может предохранить от проблем в трансляторе.

5.14 Как разделять инструкции

Существует два случая, когда Вы должны определить, как происходит расчленение команды на много insn. На машинах, имеющих команды, нуждающиеся в задержке (см. Слот задержки) или команды, вывод которых не доступен для многократных циклов. Фазы транслятора, которые оптимизируют эти случаи должны быть способна перемещать insn в одно-командный слот задержки. Однако, некоторые insns могут генерировать более одной машинной команды. Эти insn не могут быть помещены в слот задержки.

Часто Вы можете заменить insn на список список insnов каждое из которых соответствует одной машинной команде. Недостаток - медленная трансляция, требующая большого количества памяти. Если возникающий в результате insns слишком сложен, то оптимизация в трансляции уменьшается. Если есть причины предположить, что разбиение insn улучшит команду или планирование слота задержки, то транслятор его выполняет.

Фаза комбинирования insn также разбивает мнимые (putative) insn. Если в сложном выражении, которому не находится соответствующего образца 'define_insn', три insn сливаются в один, то фаза комбинирования пытается разбить сложный образеца в два распознающихся insn. Обычно можно разбить сложный образец на два образца путем разбиения некоторого подвыражения. Тем не менее, в некоторых других случаях, таких как выполнение сложения на RISC машине большой константы в два insn, способ разбиения операции сложения на два insn машинно-зависим.

Определение 'define_split' говорит транслятору как разбивать сложный insn на несколько более простых. Это выглядит примерно так:

      (define_split
        [INSN-PATTERN]
        'CONDITION'
        [NEW-INSN-PATTERN-1
         NEW-INSN-PATTERN-2
         ...]
        'PREPARATION STATEMENTS')
INSN-PATTERN - образец который должен быть разбит, а CONDITION конечное условие тестирования, как в 'define_insn'. Когда insn, ответствующий INSN-PATTERN и удовлетворяющий COONDITION, найден, он заменяется в списке insn на insn NEW-INSN-PATTERN-1, NEW-INSN-PATTERN-2, и т.д.

PREPARATION STATEMENTS подобны тем операторам, которые, определены для 'define_expand' и выполняются прежде, чем сгенерирован новый RTL, prepare for the generated code or emit some insns whose pattern is not fixed. В отличие от тех выражений `define_expand', эти выражения не должны генерировать никаких новых псевдо регистров. Как только перезагрузка завершена, они также не должны выделять память в странице стека.

Образцы сравниваются с INSN-PATTERN в двух различных случаях. Если insn должен быть разбит для планирования слота задержки или для планирования insn, то уже известно, что insn корректен. Это означает, что ему должен соответствовать некий 'define_insn' и если `reload_completed' не ноль, известно, что он удовлетворяет ограничениям этого 'define_insn'. В этом случае новым insn-образцам так же должны соответствовать неким 'define_insn', и если `reload_completed' не ноль, должны так же удовлетворять ограничениям этих определений.

Как пример этого использования `define_split', рассмотрим следующий пример из `a29k.md', который разбивает `sign_extend' из `HImode' в `SImode' в пару insn сдвига:

      (define_split
        [(set (match_operand:SI 0 'gen_reg_operand' '')
              (sign_extend:SI (match_operand:HI 1 'gen_reg_operand' '')))]
        ''
        [(set (match_dup 0)
              (ashift:SI (match_dup 1)
                         (const_int 16)))
         (set (match_dup 0)
              (ashiftrt:SI (match_dup 0)
                           (const_int 16)))]
        '
      { operands[1] = gen_lowpart (SImode, operands[1]); }')
Когда фаза комбинирования пытается разбить insn-образец, это всегда означает, что образцу *не* соответствует никакой `define_insn'. Проход комбинирования сперва пытается разбить одиночное выражение 'set' и затем то же выражение 'set' внутри 'parallel', после которого идет клоббирование псевдо регистра, что бы использовать его как 'scratch' регистр. В этих случаях комбайнер точно знает, какие два новых insn-образца должны быть сгенерированы. Он проверит, соответствуют ли эти образцы каким-нибудь 'define_insn'-определениям, поэтому Вам не нужно делать эту проверку в 'define_split' (конечно, бессмысленно писать 'define_split', который никогда не сгенерирует соответствующих insn).

Далее идет пример такого использования `define_split', взятый из `rs6000.md':

      (define_split
        [(set (match_operand:SI 0 'gen_reg_operand' '')
              (plus:SI (match_operand:SI 1 'gen_reg_operand' '')
                       (match_operand:SI 2 'non_add_cint_operand' '')))]
        ''
        [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 3)))
         (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 4)))]
      '
      {
        int low = INTVAL (operands[2]) & 0xffff;
        int high = (unsigned) INTVAL (operands[2]) >> 16;
 
        if (low & 0x8000)
          high++, low |= 0xffff0000;
 
        operands[3] = gen_rtx (CONST_INT, VOIDmode, high << 16);
        operands[4] = gen_rtx (CONST_INT, VOIDmode, low);
      }')
Здесь предикат 'non_add_cint_operand' соответствует любому ' const_int ' который является *не* корректным операндом insn одиночного сложения. Сложение с меньшим смещением написано так, чтобы оно могло замениться на адрес последующей операции.

Пример из того же самого файла, который использует scratch регистр, генерирует сравнение равенства регистра и большой константы:

      (define_split
        [(set (match_operand:CC 0 'cc_reg_operand' '')
              (compare:CC (match_operand:SI 1 'gen_reg_operand' '')
                          (match_operand:SI 2 'non_short_cint_operand' '')))
         (clobber (match_operand:SI 3 'gen_reg_operand' ''))]
        'find_single_use (operands[0], insn, 0)
         && (GET_CODE (*find_single_use (operands[0], insn, 0)) == EQ
             || GET_CODE (*find_single_use (operands[0], insn, 0)) == NE)'
        [(set (match_dup 3) (xor:SI (match_dup 1) (match_dup 4)))
         (set (match_dup 0) (compare:CC (match_dup 3) (match_dup 5)))]
        '
      {
        /*Берет константу С, с которой мы сравниваем и смотрим, на что она
          похоже, если ее расширить до 16 бит. Потом посмотрим,
          какую константу можно ХОR с С, чтобы получить расширенное знаком
          значение. */
 
        int c = INTVAL (operands[2]);
        int sextc = (c << 16) >> 16;
        int xorv = c ^ sextc;
 
        operands[4] = gen_rtx (CONST_INT, VOIDmode, xorv);
        operands[5] = gen_rtx (CONST_INT, VOIDmode, sextc);
      }')
Чтобы избежать путаницы, не пишите один 'define_split', принимающий как insns, соответствующие некоторому 'define_insn ', так и несоответствующие. Вместо этого надо писать два отдельных 'define_split'-определения, одно для корректных insns, другое для некорректных.

5.15 Атрибуты команд

В дополнение к описанию команд, поддерживаемых целевой машиной, файл 'md' также определяет группу 'атрибутов' и набор значений для каждого из них. Каждому сгенерированному insn назначено значение для каждого атрибута. Возможно будет один атрибут, показывающий что insn обманывает (has on) машинный код условия. Этот атрибут 'NOTICE_UPDATE_CC' может затем использоваться для отслеживания кодов условий.

Определение атрибутов и их значений.

Выражение `define_attr' используется для того, чтобы определить каждый атрибут, требуемый целевой машиной. Это выглядит так:

      (define_attr NAME LIST-OF-VALUES DEFAULT)
NAME - строка, определяющая имя определяемого атрибута.

LIST-OF-VALUES является либо строкой, определяющей через запятую список значений, которые могут быть назначены атрибуту, либо пустой строкой, которая указывает, что атрибут берет числовые значения.

DEFAULT - аттрибут-выражение, дающее значение этого атрибута для insns, которым соответствуют образцы, чье определение не включает в себя точного значения для этого атрибута. см. Пример Attr, для более полного описания обработки default. см. Постоянные ограничения, для информации по атрибутам, не зависящим ни от каких конкретных insn.

Для каждого определенного атрибута, ряд областей определения записан в файле 'insn-attr.h'. Для тех случаев, когда для атрибута определен явный набор значений, определено следующее:

  • Описан `#define' для символа `HAVE_ATTR_NAME'.
  • Определен перечисляемый класс для 'attr_NAME' с элементами формы `UPPER-NAME_UPPER-VALUE', где имя и значение атрибута сначала преобразуются в верхний регистр.
  • Функция `get_attr_NAME' определена так, что ей передается insn и она возвращает значение атрибута этого insn.
Например, в 'md' файле присутствует следующее:

      (define_attr 'type' 'branch,fp,load,store,arith' ...)
следующие строки будут записаны в файл 'insn-attr.h'.

      #define HAVE_ATTR_type
      enum attr_type {TYPE_BRANCH, TYPE_FP, TYPE_LOAD,
                       TYPE_STORE, TYPE_ARITH};
      extern enum attr_type get_attr_type ();
Если атрибут берет числовое значение, то 'enum' тип не будет определен, и функция получающая значение атрибута возвратит 'int'.

Атрибут-выражения

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

`(const_int I)'

Целое число I определяет значение числового атрибута. I должно быть неотрицателеным.

Значение числового атрибута может быть определено либо с помощью 'const_int' либо как целое число, представляемое строкой в выражениях `const_string', `eq_attr' (см.ниже), и `set_attr'.

`(const_string VALUE)'

Строка VALUE определяет значение атрибутов константы. Если VALUE определено как `'*'', то это означает, что по умолчанию значение атрибута должно использоваться для insn, содержащего это выражение. `'*'' очевидно не может использоваться в DEFAULT-выражении 'define_attr'.

Если атрибут, чье значение определяется - числовой, то VALUE должно быть строка, содержащая неотрицательное целое число (обычно в этом случае используется 'const_int'). Иначе, оно должно содержать одно из корректных значений атрибута.

`(if_then_else TEST TRUE-VALUE FALSE-VALUE)'

TEST определяет тест атрибута, чей формат определен ниже. Значение этого выражения - TRUE-VALUE, если ТЕST - истина и FALSE-VALUE если ложь.

`(cond [TEST1 VALUE1 ...] DEFAULT)'

Первый операнд этого выражения - массив, содержащий четное число выражений и состоящий из пар выражений TEST и VALUE. Значение выражения `cond' - это то из VALUE, которое соответствует первому истинному выражению TEST. Если все выражения TEST ложны, значение выражения `cond' - то выражение, которое установлено DEFAULT.

Выражения TEST могут иметь одну из следующих форм:

`(const_int I)'

Этот тест истинен, если I отлично от нуля, иначе ложен.

`(not TEST)'

`(ior TEST1 TEST2)'

`(and TEST1 TEST2)'

Эти тесты истины, если соответствующая логическая функция истинна.

`(match_operand:M N PRED CONSTRAINTS)'

Этот тест истинен, если N-ый операнд insn, значение атрибута которого определяется, имеет тип M (если M - 'VOIDmode' то эта часть теста игнорируется ) и функция, определенная строкой PRED возвращает не ненулевое значение, в случае если проходит операнд N и режим M (эта часть теста игнорируется, если PRED - пустая строка).

Операнд CONSTRAINTS игнорируется и должен быть пустой строкой.

`(le ARITH1 ARITH2)'

`(leu ARITH1 ARITH2)'

`(lt ARITH1 ARITH2)'

`(ltu ARITH1 ARITH2)'

`(gt ARITH1 ARITH2)'

`(gtu ARITH1 ARITH2)'

`(ge ARITH1 ARITH2)'

`(geu ARITH1 ARITH2)'

`(ne ARITH1 ARITH2)'

`(eq ARITH1 ARITH2)'

Эти тесты истины, если соответствующее сравнение из двух арифметических выражений истинно. (Арифметические выражения, сформированные с помощью выражений `plus', `minus', `mult', `div', `mod', `abs', `neg', `and', `ior', `xor', `not', `ashift', `lshiftrt', и `ashiftrt').

`const_int' and `symbol_ref' are always valid terms (см. Длины Insn). `symbol_ref' - строка, означающая выражение на С, которое оставляет 'int', сосчитанный процедурой `get_attr_...'. Обычно она должна быть глобальной переменной.

`(eq_attr NAME VALUE)'

NAME - строка, определяющая имя атрибута.

VALUE - строка, которая является либо корректным значением для атрибута NAME, либо списком значений через запятую, либо значением, за которым идет '!', либо списком. Если VALUE не начинается с '!', то этот тест истинен, когда значение атрибута NAME текущего insn находится в списке, определенном VALUE. Если ЗНАЧЕНИЕ начинается с '!', то этот тест истинен, когда значение атрибута не в определенном списке.

Например,

           (eq_attr 'type' 'load,store')
эквивалентно

           (ior (eq_attr 'type' 'load') (eq_attr 'type' 'store'))
Если NAME определяет атрибут `alternative', то он относится к значению переменной транслятора ' which_alternative ' (* note тверждение Вывода примечания::.) и значения должны быть небольшими целыми числами.

Если NAME определяет атрибут `alternative', то это ссылка на значение переменной транслятора `which_alternative' (см. Оператор вывода) и значение должно быть маленьким целым числом. Например:

           (eq_attr 'alternative' '2,3')
эквивалентно

           (ior (eq (symbol_ref 'which_alternative') (const_int 2))
                (eq (symbol_ref 'which_alternative') (const_int 3)))
Обратите внимание, что для большинства атрибутов тест `eq_attr' упрощается в случаях, когда значение проверяемого атрибута известно для всех insns, соответствующих специфическому образцу. Это намного более общий случай.

`(attr_flag NAME)'

Значение выражения `attr_flag' истинна, если флаг, определенный с помощью NAME - истина для 'insn', который планируется в настоящее время.

NAME - строка, указывающая один из флагов фиксированного набора, для проверки. Проверьте флаги `forward' и `backward' чтобы определить направление условного перехода (conditional branch). Проверите флаги `very_likely', `likely', 'very_unlikely', and `unlikely', что бы определить велика ли вероятность этого перехода .

Если флаг 'very_likely' истинен, то флаг 'likely' также истинен. Аналогично для флагов `very_unlikely' и `unlikely'.

Этот пример описывает слот задержки условного перехода, который может быть аннулирован для прямых (forward) ветвей которые исполняются (annul-true) или для обратных (backward) ветвей, которые не исполняются (annul-false).

           (define_delay (eq_attr 'type' 'cbranch')
             [(eq_attr 'in_branch_delay' 'true')
              (and (eq_attr 'in_branch_delay' 'true')
                   (attr_flag 'forward'))
              (and (eq_attr 'in_branch_delay' 'true')
                   (attr_flag 'backward'))])
Флаги `forward' и `backward' - ложь, если текущий планируемый 'insn' не является условным переходом.

Флаги `very_likely' и `likely' истинны, если планируемый 'insn' не является условным переходом. Флаги `very_unlikely' и `unlikely' ложны, если планируемый 'insn' не является условным переходом.

'Attr_flag' используется только в течение планирования слота задержки и не имеет значения для других проходов транслятора.

Установка значения атрибута для insns.

Значение, присвоенное атрибуту insn, предварительно определено тем, какой из образцов согласован с этим insn (или который `define_peephole' сгенерировал его). Каждый `define_insn' и `define_peephole' может иметь необязательный параметр, определяющий значения атрибутов для соответствующих insn. Атрибуту, не определенному ни в каком insn, устанавливается значение 'по умолчанию', как определено в его `define_attr'. Широкое использование атрибутов 'по умолчанию', дает возможность определять значения только одного-двух атрибутов большинства insn образцов, что видно в примере из следующего раздела.

Последний аргумент `define_insn' и `define_peephole' (его можно не использовать) - это массив выражений, каждое из которых определяет значение единственного атрибута. Наиболее общий способ присвоения значения атрибуту - это использование выражения `set', первый операнд которого - `attr'-выражение, задающее имя устанавливаемого аргумента. Второй операнд `set' - выражение задающее значение атрибута, (см. Выражения).

Когда значение атрибута зависит от атрибута `alternative' (то есть, который является соответствующим вариантом в ограничении insn), может использоваться выражение `set_attr_alternative '. Это позволяет специфицировать один вектора атрибутного выражения для всех вариантов.

Когда не требуется общность произвольных атрибутных выражений, может использоваться более простое выражение `set_attr', которое позволяет определить строку, дающую либо одиночное значение атрибута либо список значений атрибута по одному на каждый вариант.

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

`(set_attr NAME VALUE-STRING)'

VALUE-STRING либо строка, дающая требуемое значение атрибута, либо строка, содержащая список через запятую, дающий значения для удачных вариантов. Число элементов должно соответствовать числу вариантов в ограничении образца insn.

Обратите внимание, что может быть полезно определить '*' для некоторого варианта, и в таком случае атрибут примет значение по умолчанию для insns, соответствующего этому варианту.

`(set_attr_alternative NAME [VALUE1 VALUE2 ...])'

В зависимости от варианта insn, значение будет одним из определенных значений. This is a shorthand for using a `cond' with tests on the `alternative' attribute.

`(set (attr NAME) VALUE)'

Первый операнд 'set' должен быть специальным выражением RTL - 'attr', чей единственный операнд - строка, дающая имя устанавливаемому атрибуту. VALUE - значение атрибута.

Далее показаны три различных пути представления той же самой спецификации значения атрибута:

      (set_attr 'type' 'load,store,arith')
 
      (set_attr_alternative 'type'
                            [(const_string 'load') (const_string 'store')
                             (const_string 'arith')])
 
      (set (attr 'type')
           (cond [(eq_attr 'alternative' '1') (const_string 'load')
                  (eq_attr 'alternative' '2') (const_string 'store')]
                 (const_string 'arith')))
Выражение `define_asm_attributes' обеспечивает механизм определения атрибутов, назначенных insns, который получен из asm-операторов. Оно имеет следующую форму:

      (define_asm_attributes [ATTR-SETS])
где ATTR-SETS определен также как и для обоих выражений 'define_insn' и `define_peephole '.

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

Спецификация для атрибута `length' обрабатывается особенно. Способ вычисления длины 'asm' insn состоит в умножении длины, определенной в выражении 'define_asm_attributes' на число машинных команд, определенных в 'asm' операторе, определяемое с учетом числа точек с запятой и newlins в строке. Следовательно, значение атрибута 'length', определенного в 'define_asm_attributes' должно быть максимальной возможной длиной одиночной машинной команды.

Примеры спецификаций атрибута

Разумное использование значений по умолчанию важно для эффективного использовании insn атрибутов. Обычно, insns разделены на 'типы' и атрибут, обычно называемый 'type', используемый, для представления это значение. Этот атрибут обычно используется только, чтобы определить значение по умолчанию для других атрибутов. Пример разъяснит это использование.

Предположим, что мы имеем RISC машину с кодом условия и в которой в регистрах выполняются только операции с полным словом. Давайте предположим, что мы можем разделить все insns на загружающие, запоминающие, выполняющие (целые) арифметические операции, операции с плавающей точкой и условные.

Сейчас мы займемся определением действия insn на код условия и ограничим себя следующими возможными эффектами: Код условия может быть установлен непредсказуемо (clobbered), не измениться, быть установленным в зависимости от результатов операций, или изменяться только, если элемент предварительно установленный в коде условия был изменен.

Далее идет часть выборки файла 'md' для такой машины:

      (define_attr 'type' 'load,store,arith,fp,branch' (const_string 'arith'))
 
      (define_attr 'cc' 'clobber,unchanged,set,change0'
                   (cond [(eq_attr 'type' 'load')
                              (const_string 'change0')
                          (eq_attr 'type' 'store,branch')
                              (const_string 'unchanged')
                          (eq_attr 'type' 'arith')
                              (if_then_else (match_operand:SI 0 '' '')
                                            (const_string 'set')
                                            (const_string 'clobber'))]
                         (const_string 'clobber')))
 
      (define_insn ''
        [(set (match_operand:SI 0 'general_operand' '=r,r,m')
              (match_operand:SI 1 'general_operand' 'r,m,r'))]
        ''
        '@
         move %0,%1
         load %0,%1
         store %0,%1'
        [(set_attr 'type' 'arith,load,store')])
Обратите внимание, что мы принимаем в вышеупомянутом примере, что арифметические операции, выполняемые в количествах меньше чем машинное слово clobber код условия, так как они установят код условия к значению, соответствующему полноно-словному результату.

Вычисление Длины Insn

На многих машинах предусмотрены многочисленные типы инструкций ветвления, каждый для своей длины условного смещения. В большинстве случаев, ассемблер выберет правильную инструкцию. Однако, когда ассемблер не может этого сделать, GCC может, если специальный атрибут, атрибут 'length', определен. Этот атрибут должен по определению иметь числовые значения определяемые при помощи записи пустой строки в его `define_attr'.

В случае атрибута 'length', в выражениях теста допускаются две дополнительные формы арифметических условий :

`(match_dup N)'

Это выражение обращается к адресу операнда N текущего insn, который должен быть 'label_ref'.

`(pc)'

Она ссылается на адрес *текущего* insn. Может было бы лучше сделать ее адресом следующего insn, но это было бы неудобным, потому что длина текущего insn должна быть сосчитана.

Для нормальных insn длина будет определятся значением атрибута 'length'. В случае insn-образцов `addr_vec' и `addr_diff_vec' длина вычисляется как число векторов умноженное на размер каждого вектора.

Длины измеряются в байтах (наименьших адресуемых блоках).

Следующие макрокоманды могут использоваться, для совершенствования вычисления длины:

`FIRST_INSN_ADDRESS'

Когда используется insn-атрибут 'length', эта макрокоманда определяет значение, которое должно быть назначено адресу первого insn-а в функции. Если не определена, используется 0.

`ADJUST_INSN_LENGTH (INSN, LENGTH)'

Если определена, то она меняет длину, назначенную для инструкции INSN как функцию от контекста в котором она используется. LENGTH - lvalue, содержащее изначально вычисленную длину insn-а. Это значение следует обновлять, используя правильную длину insn. Если требуется обновление, INSN не должна быть insn меняющей длину (varying-length insn).

Эта макрокоманда обычно не требуется. Случай, когда она требуется - ROMP. На этой машине размер `addr_vec' insn должен быть увеличен на два, чтобы компенсировать тот факт, что может потребоваться выравнивание.

Подпрограмма, которая возвращает `get_attr_length' (значение атрибута 'length') может быть использована выходной подпрограммой, что бы определить форму нужной инструкции ветвления, как показано в примере ниже.

Как пример определения переходов различной длины, рассмотрим IBM 360. Если мы примем соглашение, что в регистре будет установлен начальный адрес функции, то мы можем прыгать на метки в 4-кб от старта, используя 4-байтную инструкцию. В противном случае, мы будем нуждаться в 6-байтной последовательности для загрузки адреса из памяти и выполнения перехода.

На таких машинах образец для инструкции ветвления может быть определен как следующий:

      (define_insn 'jump'
        [(set (pc)
              (label_ref (match_operand 0 '' '')))]
        ''
        '*
      {
         return (get_attr_length (insn) == 4
                 ? \'b %l0\' : \'l r15,=a(%l0); br r15\');
      }'
        [(set (attr 'length') (if_then_else (lt (match_dup 0) (const_int 4096))
                                            (const_int 4)
                                            (const_int 6)))])

Постоянные атрибуты

Специальная форма `define_attr', где выражение для значения по умолчанию является 'постоянным' выражением, указывает атрибут, являющийся константой для данного запуска транслятора. Постоянные атрибуты могут быть использованы для для определения того, какая разновидность используется. Например:

      (define_attr 'cpu' 'm88100,m88110,m88000'
       (const
        (cond [(symbol_ref 'TARGET_88100') (const_string 'm88100')
               (symbol_ref 'TARGET_88110') (const_string 'm88110')]
              (const_string 'm88000'))))
 
      (define_attr 'memory' 'fast,slow'
       (const
        (if_then_else (symbol_ref 'TARGET_FAST_MEM')
                      (const_string 'fast')
                      (const_string 'slow'))))
Подпрограмма сгенерированная для постоянных атрибутов не имеет параметров, т.к. она не зависит ни от каких конкретных insn-ов. RTL-выражения, использующиеся для определения значения постоянного атрибута, могут использовать форму `symbol_ref', но не могут использовать ни форму `match_operand' ни форму `eq_attr', включающие insn атрибуты.

Планирование слота задержки

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

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

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

Требование insn, нуждающегося в одном или более слотах задержки отображается через выражение `define_delay'. Это имеет следующую форму:

      (define_delay TEST
                    [DELAY-1 ANNUL-TRUE-1 ANNUL-FALSE-1
                     DELAY-2 ANNUL-TRUE-2 ANNUL-FALSE-2
                     ...])
TEST - проверка атрибутов, которая указывает прилагается ли этот `define_delay' к конкретному insn. Если прилагается, то число требуемых слотов задержки определяется длиной вектора, указанного как второй аргумент. Insn, помещенный в слот задержки N, должен удовлетворять проверке атрибутов DELAY-N. ANNUL-TRUE-N - тест атрибутов, который определяет, какие insn-ы могут быть аннулированы, если ветвь - истина. Аналогично, ANNUL-FALSE-N определяет какие insn-ы в слоте задержки могут быть аннулированы, если ветвь - ложь. Если аннулирование не поддерживается этим слотом задержки, то нужно написать `(nil)'.

Например, в часто встречаемом случае, где insn-ы ветвления и вызова требуют один слот задержки, который может содержать любой insn, отличный от insn-ов ветвления и вызова, в `md' файл было бы помещено следующее:

      (define_delay (eq_attr 'type' 'branch,call')
                    [(eq_attr 'type' '!branch,call') (nil) (nil)])
Может быть определено множество выражений `define_delay'. В этом случае каждое такое выражение определяет различные требования слота задержки и не должно быть не одного insn-а, для которого верны хотя бы два из тестов выражений `define_delay'.

Например, если мы имеем машину, требующую один слот задержки для ветвлений и два для вызовов, то никакой слот задержки не может содержать insn ветвления или вызова, и любой корректный insn в слоте задержки для ветвления может быть аннулирован, если ветвь - истина, мы можем представить это следующим образом:

      (define_delay (eq_attr 'type' 'branch')
         [(eq_attr 'type' '!branch,call')
          (eq_attr 'type' '!branch,call')
          (nil)])
 
      (define_delay (eq_attr 'type' 'call')
                    [(eq_attr 'type' '!branch,call') (nil) (nil)
                     (eq_attr 'type' '!branch,call') (nil) (nil)])

Определения функциональных модулей

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

Кроме того, многие более новые микропроцессоры имеют многофункциональные модули - обычно один для целого числа и один для плавающей точки, и часто, когда требуемый результат еще не готов, будет возникать pipeline stall.

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

Для целей определения в этом разделе, машина разделена на 'функциональные модули', каждый из которых выполняет специфический класс команд в порядке 'первый-вошел-первый-вышел'. Функциональные модули, которые принимают одну команду за каждый цикл и позволяют результату использоваться в преуспевающей команде (обычно через пересылку) не должны быть определены. Классические RISC микропроцессоры будут обычно иметь одиночный функциональный модуль, который мы можем называть `memory'. Более новые 'superscalar' процессоры будут часто иметь функциональные модули для операций с плавающей точкой, обычно по крайней мере для вещественного сложения и умножения.

Каждое использование функционального модуля классом insn определяется выражением `define_function_unit', которое выглядит приблизительно так:

      (define_function_unit NAME MULTIPLICITY SIMULTANEITY
                            TEST READY-DELAY ISSUE-DELAY
                           [CONFLICT-LIST])
NAME - строка, дающая имя функционального модуля.

MULTIPLICITY - целое число, определяющее число идентичных модулей в процессоре. Если определен больше чем один модуль, то их планирование будет происходить независимо. Должны быть учтены только по-настоящему независимые модули. (Единственный общий пример машины, которая имеет несколько функциональных модулей для одного класса команды, являющиеся по-настоящему независимыми и не pipelined - два модуля умножения и два модуля сложения в CDC 6600.)

SIMULTANEITY определяет максимальный номер insns, который может выполняться в каждом образце функционального модуля одновременно, или ноль, если модуль - pipelined и не имеет никаких ограничений.

Все определения `define_function_unit', касающиеся функционального модуля NAME, должны иметь те же самые имена и значения для MULTIPLICITY и SIMULTANEITY.

TEST - тест атрибута, который выбирает insns, описываемый в этом определение. Обратите внимание, что insn может использовать более чем один функциональный модуль, а функциональный модуль может быть определен в более чем одном 'define_function_unit'.

READY-DELAY - целое число, которое определяет количество циклов, после которого результат команды может использоваться без всякой задержки.

? ISSUE-DELAY - целое число, которое определяет количество циклов после того, как команда, соответствующая выражению TEST начинает использовать этот модуль, до того как может начаться последующая команда. Цена N указывает задержку цикла номер N-1. Последующая команда может также быть отсрочена, если более ранняя команда имеет большее значение READY-DELAY. Этот эффект блокирования вычислен, используя термины SIMULTANEITY, READY-DELAY, ISSUE-DELAY, и CONFLICT-LIST. Для нормального не-pipelined функционального модуля, в частности SIMULTANEITY работает так, что взятый модуль, блокирует READY-DELAY циклов выполняющейся insn, и меньшее из значений ISSUE-DELAY игнорируется.

CONFLICT-LIST - дополнительный список, дающий уточненные цены конфликта (conflict costs) для этого модуля. Если этот список определен, то это - список выражений теста условии, которые нужно применить к insns, выбранным для выполнения в NAME, после insn который соответствует TEST, уже выполненному в NAME. Для каждого insn в списке, ISSUE-DELAY определяет цену конфликта; для insns, которого нет в списке, цена - ноль. Если этот список не определен, CONFLICT-LIST строится по умолчанию для всех команд, использующих функциональный модуль.

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

Как пример, рассмотрим классическую RISC машину, в которой результат команды загрузки не доступен в течении двух циклов (требуется одиночная команда 'delay') и в которой только одна команда загрузки может быть выполнена одновременно. Это было бы определено как:

      (define_function_unit 'memory' 1 1 (eq_attr 'type' 'load') 2 0)
Для случая плавающей точки функциональный модуль, который может pipeline либо одиночная либо двойная точность, но не обе. Иожет быть определено следующее:

      (define_function_unit
         'fp' 1 0 (eq_attr 'type' 'sp_fp') 4 4 [(eq_attr 'type' 'dp_fp')])
      (define_function_unit
         'fp' 1 0 (eq_attr 'type' 'dp_fp') 4 4 [(eq_attr 'type' 'sp_fp')])
*Замечание*: Планировщик пытается избежать конфликтов функциональных модулей и использует все определения в выражении `define_function_unit'. Недавно мы обратили внимание, что эти определения не могут позволять моделирование некоторых новейших "супер скалярных" процессоров, которые имеют insn, использующие много pipeline модулей. Эти insn могут служить причиной потенциального конфликта для второго модуля, используемого в течение их выполнения, и не существует способа представления этого конфликта. Мы приветствуем любые примеры того, как происходят конфликты функциональных модулей в таких процессорах и предположения по их представлению.


Вперед Назад Содержание

Наш баннер
Вы можете установить наш баннер на своем сайте или блоге, скопировав этот код:
RSS новости