Примеры кода QBE IL
ВАЖНО! Пожалуйста, обратите внимание, некоторые операции, приведенные в примерах, используются без "левой части" (например, %x = w ...). Это не ошибка, примеры имеют такой вид исключительно ради наглядности и конкретизации тем, обозначенных в заголовках примеров.
Операции и операнды[править]
Рассмотрим некоторые операции и операнды в 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.