Трёхадресная команда
Трёхадресная команда (трёхадресный код, трёхоперандная команда) — это форма записи машинных инструкций или промежуточного кода, в которой команда содержит три адресных поля, указывающих на операнды (обычно два источника и один приёмник результата). Является распространённой архитектурой набора команд (ISA), особенно в RISC-процессорах, а также в промежуточных представлениях компиляторов и виртуальных машинах.
История
Развитие трёхадресных команд тесно связано с эволюцией вычислительной техники и архитектур процессоров. Ранние компьютеры, такие как IAS-машина (1945) и EDSAC (1949), использовали одноадресные команды, где результат операции сохранялся в аккумуляторе. С ростом сложности вычислений и требований к производительности возникла необходимость в более гибкой адресации.
В 1950-х годах появились машины с двух- и трёхадресными командами. Например, IBM 7090 (1958) использовал трёхадресные инструкции для некоторых операций. Однако широкое распространение трёхадресная архитектура получила с развитием RISC-процессоров в 1980-х годах. Проекты, такие как MIPS (1981) и SPARC (1987), заложили основы современных трёхадресных наборов команд, где каждая арифметическая или логическая операция явно указывает три регистра.
В области компиляции трёхадресный код (TAC) стал стандартным промежуточным представлением, используемым в таких компиляторах, как GCC и LLVM, начиная с 1990-х годов. Это позволило унифицировать процесс оптимизации и генерации кода для различных целевых архитектур.
Структура трёхадресной команды
Трёхадресная команда в общем виде имеет формат:
OP DEST, SRC1, SRC2
где:
OP— код операции (например, ADD, SUB, MUL, LOAD, STORE);DEST— адрес (или регистр) для записи результата;SRC1— адрес (или регистр) первого операнда;SRC2— адрес (или регистр) второго операнда.
Примеры:
ADD R1, R2, R3— сложить содержимое регистров R2 и R3, результат записать в R1.SUB R4, R5, #10— вычесть из содержимого R5 константу 10, результат в R4.MUL R6, R7, R8— умножить R7 на R8, результат в R6.
В некоторых архитектурах (например, ARM) трёхадресные команды могут быть модифицированы для использования констант или флагов состояния.
Классификация
По типу операндов
- Регистровые трёхадресные команды — все операнды находятся в регистрах процессора. Характерны для RISC-архитектур (MIPS, ARM, RISC-V).
- Регистрово-памятные трёхадресные команды — один или два операнда могут быть в памяти, а результат также может быть записан в память. Встречаются в некоторых CISC-архитектурах (например, VAX).
- Команды с непосредственными операндами — один из операндов является константой, закодированной в самой команде.
По назначению
- Арифметические и логические — ADD, SUB, MUL, DIV, AND, OR, XOR.
- Пересылки данных — MOV, LOAD, STORE (хотя LOAD и STORE часто двухадресные).
- Управления — JMP, BEQ, BNE (условные и безусловные переходы).
- Сравнения — CMP, TEST (часто двухадресные, но могут быть трёхадресными в некоторых архитектурах).
Применение
В процессорах
Трёхадресные команды являются основой большинства современных RISC-процессоров. Основные преимущества:
- Повышение производительности — за счёт явного указания всех операндов сокращается количество обращений к памяти и уменьшается зависимость от аккумулятора.
- Конвейеризация — трёхадресные команды облегчают распараллеливание выполнения инструкций.
- Упрощение компиляции — компилятору не нужно генерировать дополнительные команды для перемещения данных между регистрами.
Примеры архитектур, использующих трёхадресные команды:
- MIPS — классическая RISC-архитектура с 32 регистрами и трёхадресными арифметическими командами.
- ARM — поддерживает трёхадресные команды в режиме ARM (32-битные инструкции), но в Thumb-режиме (16-битные) часто использует двухадресные.
- RISC-V — открытая архитектура, где базовые арифметические команды трёхадресные.
- x86-64 — в основном использует двухадресные команды (CISC-традиция), но с расширениями AVX и SSE появились трёхадресные векторные инструкции.
В компиляторах
Трёхадресный код (TAC) — это промежуточное представление программы, используемое в компиляторах для оптимизации и генерации машинного кода. Каждая инструкция TAC имеет вид:
x = y op z— арифметическая операция;x = y— копирование;goto L— безусловный переход;if x relop y goto L— условный переход;x = y[i]— доступ к массиву;x[i] = y— запись в массив.
TAC широко применяется в компиляторах GCC, LLVM, а также в виртуальных машинах (например, Dalvik в Android — до версии 5.0).
В виртуальных машинах
Некоторые виртуальные машины используют трёхадресные инструкции для обеспечения переносимости и эффективности:
- Java Virtual Machine (JVM) — байт-код JVM в основном двухадресный (стековый), но некоторые инструкции (например,
iadd) оперируют с вершиной стека. - Common Language Runtime (CLR) — платформа .NET использует стековую архитектуру, но с трёхадресными элементами в IL-коде.
- WebAssembly — использует стековую архитектуру, но с возможностью локальных переменных.
Сравнение с другими архитектурами
| Характеристика | Трёхадресная | Двухадресная | Одноадресная | Стековая |
|---|---|---|---|---|
| Число операндов | 3 | 2 | 1 | 0 (неявные) |
| Типичные архитектуры | RISC (MIPS, ARM, RISC-V) | CISC (x86, 68000) | Аккумуляторные (PDP-8) | JVM, CLR, Forth |
| Длина команды | Фиксированная (32 бит) | Переменная (1-15 байт) | Фиксированная (16-32 бит) | 1 байт (часто) |
| Производительность | Высокая (конвейер) | Средняя | Низкая | Средняя |
| Сложность компиляции | Низкая | Средняя | Высокая | Средняя |
Преимущества и недостатки
Преимущества
- Явная адресация — отсутствие скрытых зависимостей от аккумулятора или стека упрощает анализ и оптимизацию кода.
- Конвейеризация — трёхадресные команды легче распараллеливаются, что важно для суперскалярных процессоров.
- Уменьшение числа команд — для выполнения операции
a = b + cтребуется одна команда, а не две (как в двухадресной архитектуре:MOV a, b; ADD a, c). - Гибкость — компилятор может свободно распределять регистры, не заботясь о сохранении аккумулятора.
Недостатки
- Увеличение длины команды — трёхадресные команды обычно занимают 32 бита, что увеличивает объём кода по сравнению с 16-битными двухадресными командами.
- Сложность декодирования — большее число полей требует более сложного декодера.
- Ограниченность регистров — в архитектурах с малым числом регистров (например, x86-16) трёхадресные команды могут быть неэффективны.
Примеры
Пример 1: MIPS
Вычисление c = a + b на MIPS: `` lw $t0, a # загрузка a в $t0 lw $t1, b # загрузка b в $t1 add $t2, $t0, $t1 # $t2 = $t0 + $t1 sw $t2, c # сохранение результата в c ` Здесь add` — трёхадресная команда.
Пример 2: RISC-V
Вычисление d = (a + b) c: `` add t0, a, b # t0 = a + b mul d, t0, c # d = t0 c ``
Пример 3: Трёхадресный код компилятора
Исходный код на C: ``c int x = 5; int y = x + 3; int z = y 2; ` TAC: ` t1 = 5 x = t1 t2 = x + 3 y = t2 t3 = y 2 z = t3 ``
Интересные факты
- В архитектуре x86 до появления AVX-512 трёхадресные команды были редкостью. В AVX-512 появились инструкции с тремя и даже четырьмя операндами (например,
vfmadd132ps). - RISC-V — одна из немногих современных архитектур, где трёхадресные команды являются базовым стандартом, а не опцией.
- В компиляторах LLVM трёхадресный код используется в промежуточном представлении (IR), которое затем оптимизируется и транслируется в машинный код.
Источники
- Patterson, D. A., & Hennessy, J. L. (2017). Computer Organization and Design: The Hardware/Software Interface. Morgan Kaufmann.
- Tanenbaum, A. S. (2016). Structured Computer Organization. Pearson.
- Aho, A. V., Lam, M. S., Sethi, R., & Ullman, J. D. (2006). Compilers: Principles, Techniques, and Tools. Addison-Wesley.
- Документация архитектур MIPS, ARM, RISC-V.
- Статья «Three-address code» в английской Википедии.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →