Примеры кода QBE IL

Материал из Compilers Wiki
Перейти к: навигация, поиск

ВАЖНО! Пожалуйста, обратите внимание, некоторые операции, приведенные в примерах, используются без "левой части". Это не ошибка, примеры имеют такой вид исключительно ради наглядности и конкретизации тем, обозначенных в заголовках примеров.

Операции и операнды

Рассмотрим некоторые операции и операнды в QBE.

Подробнее можно прочитать в разделе Арифметические и битовые операции. Обычная арифметика - сложение (add), вычитание (sub), деление (div), умножение (mul). Каждая из этих операций требует два аргумента/операнда (очевидно).

Примеры.

add 0, 1 

К нулю (первое слагаемое/первый аргумент/операнд) прибавляется единица (второе слагаемое/второй аргумент/операнд). Результатом сложения (сумма) будет число 1.

sub 2, 1

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

div 4, 2

Четыре (делимое/первый аргумент/операнд) делится на два (делитель/второй аргумент/операнд). Результатом деления (частным) будет число 2.

mul 2, 2

Два (первый множитель/первый аргумент/операнд) умножается на два (второй множитель/второй аргумент/операнд). Результатом умножения (произведением) будет число 4.

Аналогично для логических операций. Для операций сдвига справедливо требование наличие двух аргументов/операндов. В этом случае первый аргумент/операнд будет являться тем, что требуется "сдвинуть", а второй – числом (целым), на которое требуется осуществить сдвиг.

Операции над памятью.

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

storeb 0, %array

Разместит в первый байт массива array (то есть %array выступает аналогом array[0]) число 0. Суффикс b на конце инструкции store свидетельствует о типе используемых операндов.

loadub %array

Результатом выполнения инструкции будет усеченное до unsigned (беззнаковое) значение первого байта массива array. Суффиксы u и b означают unsigned (беззнаковое) и byte (байт) соответственно. То есть суффиксы инструкции load накладывают ограничение на возвращаемый результат.

Подробнее о работе с массивами будет рассказано ниже.

Присваивания

Пример кода на Си.

int a = 1;
int b = 2;
int c;
c = a + b;

Аналогичный код на QBE IL.

%a = w add 0, 1
%b = w add 0, 2
%c = w add %a, %b

Блоки

@start
  %b = w add 0, 5
@initA
  %a = w add 0, 1
@initB
  %b = w add 0, 2
@result
  %c = w add %a, %b
@end
  ret 0

Результатом выполнения будет c = 3 , а без наличия переходов между блоками каждая инструкция будет выполняться последовательно. То есть в блоке @start в переменную b будет помещено число 5. Затем мы попадем в блок @initA, где объявим переменную a и положим в нее число 1. Далее, в блоке @initB уже размещенное в переменной b будет заменено на число 2. В блоке @result объявляется переменная c и инициализируется суммой переменных a и b, эта сумма будет равна 3 (1+2). Блок @end осуществит выход из функции.

Сравнения

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

%condition = w cslew %a, %b

Данный пример сравнивает две переменных размерности w (word) (о чем свидетельствует окончание инструкции - w) на "меньше или равно" (суффикс le означает "lower or equal") и возвращает 0 размерности w (word) в случае, если %b больше %a, либо 1 размерности w (word) в случае положительного выполнения условий суффикса.


Переходы

Рассмотрим код из предыдущего примера с "Блоками", но внесем одно изменение – установим безусловный переход jmp на другой блок в блоке @initA.

@start
  %b = w add 0, 5
@initA
  %a = w add 0, 1
  jmp @result
@initB
  %b = w add 0, 2
@result
  %c = w add %a, %b
@end
  ret 0

При выполнении, с помощью jmp @result после объявления переменной a выполнение продолжится не последовательно (в блоке @initB), а с блока @result. То есть c = 1 + 5 (не произойдет переприсваивания b = 2), тем самым, результатом в c будет являться число 6.

Заменим безусловный переход jmp @result из примера выше на переход по условию %condition, которое добавим перед переходом.

@start
  %b = w add 0, 5
@initA
  %a = w add 0, 1
  %condition = w cslew %a, 2
  jnz %condition, @result, @initB
@initB
  %b = w add 0, 2
@result
  %c = w add %a, %b
@end
  ret 0

В данном случае, условие %condition = w cslew %a, 2 всегда будет выполняться, а переход jnz %condition, @result, @initB всегда будет переходить на ветвь @result. Но если сравнение, например, в @initA в переменную %a положить не 1, а 3, то условие будет всегда ложным, тогда переход будет всегда выполняться на ветвь @initB, и результатом выполнения программы будет %c = 3 (т.к. a = 3) + 2 (т.к. в b будет лежать 2) = 5

Массивы

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

%arrayN = l add %array, %N

Теперь с помощью arrayN и инструкций load и store можно получить и записать (соответственно) в N-ный элемент массива array.