В інформатиці триадресний код[1] (часто скорочено до TAК (TAC англійською) — це проміжний код, який використовується оптимізувальними компіляторами для допомоги в реалізації перетворень, що покращують код (оптимізацій). Кожна інструкція TAC має не більше трьох операндів, і зазвичай є поєднанням оператора присвоєння та бінарного оператора. Наприклад, t1 := t2 + t3. Назва походить від використання трьох операндів у цих операторах, хоча можуть зустрічатися інструкції з меншою кількістю операндів.

Оскільки триадресний код використовують як проміжну мову в компіляторах, операндами напевно будуть не конкретні адреси пам'яті чи регістри процесора, а скоріше символічні адреси, які будуть перетворені у реальні адреси під час розподілу регістрів. Також нерідко імена операндів нумеруються послідовно, оскільки триадресний код як правило генерується компілятором.

Уточненням триадресного коду є A-нормальна форма (ANF).

Види кодів ред.

Присвоєння (Assignments) ред.

Існують два можливих варіанти:

  1. x := y op z, де op — це бінарна арифметична або логічна операція. Наприклад: x := y + z (додавання), x := y * z (множення), x := y and z (логічне І).
  2. x := op y, де op — це унарна операція (мінус, заперечення, оператор конвертації). Наприклад: x := -y (мінус y), x := not y (заперечення y), x := int(y) (конвертація в ціле число).

Операції копіювання (Copy statements) ред.

Мають форму x := y, де значення, яке міститься у змінній y, копіюється до змінної x. Це проста операція, що дозволяє копіювати значення однієї змінної в іншу. Наприклад, якщо маємо змінну y зі значенням 5, операція x := y приведе до того, що змінна x також буде мати значення 5.

Безумовні переходи (Unconditional jumps) ред.

Мають форму «goto L», де L — символьна мітка (label) для позначення інструкції або блоку коду. Ці операції використовують для безумовного переходу у місце в програмі, інтерпретуючи символьну мітку як місце призначення переходу.

Умовні переходи (Conditional jumps) ред.

Мають форму «if x relop y goto L», де x та y — це операнди, relop — це оператор відношення (наприклад, рівність, нерівність, менше, більше тощо), а L — символьна мітка (label) вказує на місце в програмі, куди буде виконано перехід у випадку, якщо умова відповідає вказаному відношенню.

Виклики процедур (Procedure calls) ред.

Мають форму

param x1
param x2
...
param xn
call p, n

де x1, x2, ..., xn — це аргументи, які передаються у процедуру p, а n — кількість аргументів.

Ці інструкції вказують компілятору, щоб підготував аргументи x1, x2, ..., xn у відповідному стані для виклику процедури p (наприклад, зберіг дані в регістрах або скопіював у спеціальну буферну зону) і потім здійснив виклик процедури з цими аргументами. При виклику процедури p з аргументами x1, x2, ..., xn, виконуються інструкції, які визначені у тілі процедури.

Після виконання процедури, потік виконання повертається до місця після інструкції call.

Інструкції повернення (Return statements) ред.

Мають форму «return y», де y — це значення, що буде повернено із процедури. Значення у є опціональним.

Індексовані присвоєння (Indexed assignments) ред.

Мають дві форми x := y[i] або x[i] := y.

Адресні присвоєння (Address assignments) ред.

Мають форму x := &y. Після операції в змінній x буде записана адреса змінної y

Присвоєння з використанням вказівників (Pointer assignments) ред.

  • x := *y: Змінна x отримує значення, на яке вказує вказівник y
  • *x := y: Змінює місце знаходження значення змінної y, яке вказується вказівником x.

Приклади ред.

У триадресному коді будь-який вираз може бути перетворені на кілька окремих інструкцій. Ці інструкції пізніше легше перекладати на мову асемблера. Також легше виявити спільні підвирази для скорочення коду.

Нижче наведені приклади обчислення рішення для квадратного рівняння. У триадресному коді те ж саме обчислення складається з кількох дрібних обчислень:

# Calculate one solution to the [[Quadratic equation]].
x = (-b + sqrt(b^2 - 4*a*c)) / (2*a)
t1 := b * b
t2 := 4 * a
t3 := t2 * c
t4 := t1 - t3
t5 := sqrt(t4)
t6 := 0 - b
t7 := t5 + t6
t8 := 2 * a
t9 := t7 / t8
x := t9

Триадресний код може містити умовні та безумовні переходи, також методи доступу до пам'яті. Він також може містити методи виклику функцій. Таким чином, триадресний код може бути корисним для аналізу потоку керування. У наведеному нижче прикладі на мові схожій до С, цикл зберігає квадрати чисел від 0 до 9:

...

for (i = 0; i < 10; ++i) {
    b[i] = i*i; 
}

...
     t1 := 0                ; initialize i
L1:  if t1 >= 10 goto L2    ; conditional jump
     t2 := t1 * t1          ; square of i
     t3 := t1 * 4           ; word-align address
     t4 := b + t3           ; address to store i*i
     *t4 := t2              ; store through pointer
     t1 := t1 + 1           ; increase i
     goto L1                ; repeat loop
L2:

Застосування ред.

Здебільшого триадресні коди дуже схожі на асемблерні інструкції, тому цей код використовують в компіляторах.

Оптимізація ред.

Триадресний код часто використовують як проміжне представлення коду в різних фазах оптимізації під час компіляції програм. Триадресний код дозволяє компілятору аналізувати код та здійснювати оптимізації, які можуть покращити ефективність вихідного коду.

Генерація коду ред.

Триадресний код також можуть використовувати як проміжне представлення коду в фазах генерації коду під час компіляції. Триадресний код дозволяє компілятору генерувати код, що специфічний для цільової платформи, забезпечуючи при цьому правильність та ефективність вихідного коду.

Див. також ред.

Джерела ред.

  1. V., Aho, Alfred (1986). Compilers, principles, techniques, and tools. Reading, Mass.: Addison-Wesley Pub. Co. с. 466. ISBN 0201100886. OCLC 12285707.