- •Лабораторна робота № 4 Генерація і оптимізація об'єктнго кода
- •Короткі теоретичні відомості
- •Побудова асемблерного коду по дереву виводу
- •Побудова списку тріад по дереву виводу.
- •Оптимізація об'єктного коду методом виключення зайвих операцій
- •Загальний алгоритм генерації і оптимізації об'єктного коду
Побудова асемблерного коду по дереву виводу
Як мова асемблера візьмемо мову асемблера процесорів типу Intel 80x86. При цьому вважатимемо, що операнди можуть бути поміщені в 16-розрядні регістри процесора і в коді результуючої об'єктної програми можуть використовуватися регістри AX (акумулятор) і DX (регістр даних), а також стік для зберігання проміжних результатів.
Тоді чотирьом формам поточного вузла дерева відповідатимуть наступні фрагменти коди на мові асемблера (табл. 5):
Таблиця 5.
Перетворення типових вузлів дерева виводу в код на мові асемблера
Вид вузла дерева |
Результуючий код |
Примітка |
|
mov ax,oper1 act ax,oper2 |
act - команда відповідної операції oper1,oper2 - операнди (листя дерева) |
|
Code(Узел 2) mov dx,ax mov ax,oper1 act ax,dx |
Узел 2 - вузол (не лист!) дерева, що пролягає нижче Code(Узел 2) - код, що створюються процедурою для вузла, що пролягає нижче |
|
Code(Узел 2) act ax,oper2 |
Code(Узел 2) - код, що створюються процедурою для вузла, що пролягає нижче |
|
Code(Узел 2) push ax Code(Узел 3) mov dx,ax pop ax act ax,dx |
Code(Узел 2) - код, що створюються процедурою для вузла, що пролягає нижче Code(Узел 3) - код, що створюються процедурою для вузла, що пролягає нижче push і pop - команди збереження результатів в стеку і витягання результатів із |
Розглянемо приклад дерева виводу для виразу A := B*c + D - B*10 на мал. 5 і відповідний йому фрагмент коди на мові асемблера, побудований по описаних вище правилах (звернете увагу, що для операції привласнення використовується окремий код, що не підпадає під загальні правила):
Мал. 5. Дерево
виводу для арифметичного
виразу.
mov A,ax ; операція привласнення
Крок 2: Code(U3)
push ax
Code(U5)
mov dx,ax
pop ax
sub ax,dx
mov A,ax ; операція привласнення
Крок 3: Code(U4)
add ax,D
push ax
Code(U5)
mov dx,ax
pop ax
sub ax,dx
mov A,ax ; операція привласнення
Крок 4: mov ax,B
mul ax,C
add ax,D
push ax
Code(U5)
mov dx,ax
pop ax
sub ax,dx
mov A,ax ; операція привласнення
Крок 5: mov ax,B
mul ax,C
add ax,D
push ax
mov ax,B
mul ax,10
mov dx,ax
pop ax
sub ax,dx
mov A,ax ; операція привласнення
Отриманий об'єктний код на мові асемблера, очевидно, може бути оптимізований, проте для його обробки потрібні спеціальні (орієнтовані саме на дану мову асемблера) методи і структури, що враховують взаємозв'язок операцій. Крім того, орієнтація на певну мову асемблера зводить нанівець універсальність методу. Так в приведеному прикладі використовується команда mul, яка в ранніх версіях процесорів фірми Intel має обмеження на типи операндів, а отже, в універсальному компіляторі не може бути використана так, як в даному прикладі. Це зажадає, щоб генерація коди для вузлів дерева йшла в залежності не тільки від операндів, але і від типу операції (навіть у приведеному прикладі таку залежність довелося встановити для операції привласнення).
Зазвичай такі проблеми вирішуються таким чином, що замість команд безпосередньо мови асемблера використовуються команди деякого близького до нього проміжного псевдокоду. Більшість цих команд один в один відображаються потім в команди мови асемблера, інші ж однозначно перетворяться у фіксовану послідовність команд.
