
ЛР4 Саляхов
.docxМинистерство образования и науки Российской Федерации
Федеральное государственное бюджетное образовательное учреждение высшего образования
«Уфимский университет науки и технологий»
Кафедра ТК
Отчет по лабораторной работе № 4
«Системное программное обеспечение»
Тема: «Генерация и оптимизация объектного кода» Вариант 11
Выполнил:
студент группы ИВТ-429Б
Саляхов А.Ф.
Проверил:
доцент кафедры ТК
Ракипова А.С.
Уфа 2024
Цель работы: изучение основных принципов генерации компилятором объектного кода, ознакомление с методами оптимизации результирующего объектного кода для линейного участка с помощью свертки и исключения лишних операций.
Задание: для выполнения лабораторной работы требуется написать программу, которая на основании дерева синтаксического разбора порождает объектный код и выполняет затем его оптимизацию методом свертки объектного кода и методом исключения лишних операций. В качестве исходного дерева синтаксического разбора рекомендуется взять дерево, которое порождает программа, построенная по заданию лабораторной работы № 3.
Программу рекомендуется построить из трех основных частей: первая часть порождение дерева синтаксического разбора (по результатам лабораторной работы № 3), вторая часть — реализация алгоритма порождения объектного кода по дереву разбора и третья часть — оптимизация порожденного объектного кода (если в результирующей программе присутствуют линейные участки кода). Результатом работы должна быть построенная на основе заданного предложения грамматики программа на объектном языке или построенная последовательность триад(по согласованию с преподавателем выбирается ка представления конечного результата).
В качестве объектного языка предлагается взять язык ассемблера для процессоров типа Intel 80х86 в реальном режиме (возможен выбор другого объектного языка по согласованию с преподавателем). Все встречающиеся в исходной программе идентификаторы считать простыми скалярными переменными, не требующими выполнения преобразования типов. Ограничения на длину идентификаторов и констант соответствуют требованиям лабораторной работы № 3.
Вариант:
Входной язык содержит операторы условия типа if ... then .. else и if .. then, разделённые символом ; (точка с запятой). Операторы условия содержат идентификаторы, знаки сравнения <, >, =, шестнадцатеричные числа, знак присваивания (:=)
Запись грамматики входного языка в форме Бэкуса- Наура:
G ({if, then, else, i, ;, <, >, =, O, :=},{S, F, T, E }, P, S)
P:
S→ F;
F→ if E then T else F | if E then F | i := O
T→ if E then T else T | i := O
E→ i < i | i > i | i = i
Описание схем СУ-перевода для операций исходного языка в соответствии с заданной грамматикой
Все операции, которые могут присутствовать во входной программе на языке, заданном грамматикой G, по смыслу (семантике) можно разделить на следующие группы:
Знаки сравнения (<, >, =);
оператор присваивания (:=);
операции, не несущие смысловой нагрузки, а служащие только для создания синтаксических конструкций исходной программы (в данном языке таких операций две: круглые скобки и точка с запятой).
Рассмотрим схемы СУ-перевода для всех перечисленных групп операций.
Процедура генерации триад по синтаксическому дереву прежде всего должна определить тип узла дерева. Для бинарных арифметических операций каждый узел дерева имеет три нижележащие вершины (левая вершина — первый операнд, средняя вершина — операция и правая вершина — второй операнд). При этом тип узла дерева соответствует типу операции, символом которой помечена средняя из нижележащих вершин. После определения типа узла процедура строит триады для узла дерева в соответствии с типом операции.
Фактически процедура генерации триад должна для каждого узла дерева выполнить конкатенацию триады, связанной с текущим узлом, и цепочек триад, связанных с нижележащими узлами. Конкатенация цепочек триад должна выполняться таким образом, чтобы триады, связанные с нижележащими узлами, выполнялись до выполнения операции, связанной с текущим узлом. Причем для арифметических операций важно, чтобы триады, связанные с первым операндом, выполнялись раньше, чем триады, связанные со вторым операндом.
При этом возможны четыре ситуации:
левая и правая вершины указывают на непосредственный операнд (это можно определить, если у каждой из них есть только один нижележащий узел, помеченный символом какой-то лексемы — константы или идентификатора);
левая вершина является непосредственным операндом, а правая указывает на другую операцию;
левая вершина указывает на другую операцию, а правая является непосредственным операндом;
обе вершины указывают на другую операцию.
Считаем, что на вход процедуры порождения триад по синтаксическому дереву подается список, в который нужно добавлять триады, и ссылка на узел дерева, который надо обработать. Тогда процедура порождения триад для узла синтаксического дерева, связанного с бинарной арифметической операцией, может выполняться по следующему алгоритму:
1. Проверяется тип левой вершины узла. Если она — простой операнд, запоминается имя первого операнда, иначе для этой вершины рекурсивно вызывается процедура порождения триад, построенные ею триады добавляются в конец общего списка и запоминается номер последней триады из этого списка как первый операнд;
2. Проверяется тип правой вершины узла. Если она — простой операнд, запоминается имя второго операнда, иначе для этой вершины рекурсивно вызывается процедура порождения триад, построенные ею триады добавляются в конец общего списка и запоминается номер последней триады как второй операнд.
3. В соответствии с типом средней вершины в конец общего списка добавляется триада, соответствующая арифметической операции. Ee первым операндом становится операнд, запомненный на шаге 1, а вторым операндом — операнд, запомненный на шаге 2.
4. Процедура закончена.
Для оператора присваивания схема СУ-перевода аналогична.
Операции, которые не несут никакой смысловой нагрузки, не требуют построения результирующего кода. Для них не требуется строить схемы СУ-перевода.
Пример генерации и оптимизации последовательности триад на основе простейшей исходной программы
Возьмем в качестве примера входную цепочку:
if A > B then P := X else if A > B then P := X else if A = B then H := V;
В результате этому дереву будет соответствовать следующая последовательность триад:
0: > (A, B)
1: > (A, B)
2: = (A, B)
3: := (P, X)
4: := (P, X)
5: := (H, V)
6: then (^3, )
7: then (^4, )
8: then (^5, )
9: if (^0, ^6)
10: if (^1, ^7)
11: if (^2, ^8)
12: else (^9, ^10)
13: else (^12, ^11)
Код программы:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cctype>
#include <stack>
#include <unordered_map>
#include <algorithm>
#include <queue>
#include <sstream>
#include <set>
using namespace std;
bool hasUnknownLexeme = false;
struct Lexeme {
string value;
string type;
string chain;
int lineNumber;
};
struct Triad {
string operation;
string operand1;
string operand2;
Triad(const string& op, const string& op1, const string& op2)
: operation(op), operand1(op1), operand2(op2) {}
bool operator==(const Triad& other) const {
return operation == other.operation &&
operand1 == other.operand1 &&
operand2 == other.operand2;
}
};
vector<Triad> triads;
vector<Triad> uniqueTriads;
struct TriadHasher {
size_t operator()(const Triad& triad) const {
size_t h1 = hash<string>{}(triad.operation);
size_t h2 = hash<string>{}(triad.operand1);
size_t h3 = hash<string>{}(triad.operand2);
return h1 ^ (h2 << 1) ^ (h3 << 2);
}
};
namespace std {
template<>
struct hash<Triad> {
size_t operator()(const Triad& t) const {
return hash<string>()(t.operation) ^ hash<string>()(t.operand1) ^ hash<string>()(t.operand2);
}
};
}
string determineLexemeType(const string& lexeme) {
if (!lexeme.empty() && isdigit(lexeme[0])) {
bool isHex = true;
for (char ch : lexeme) {
if (!isxdigit(ch)) { // Проверяем, является ли символ цифрой или буквой A-F/a-f
isHex = false;
break;
}
}
if (isHex) return "Шестнадцатеричное число";
}
// Проверки для других типов
if (lexeme == "if" || lexeme == "then" || lexeme == "else") return "Оператор условия";
if (lexeme == "<" || lexeme == ">" || lexeme == "=") return "Знак сравнения";
if (lexeme == ":=") return "Оператор присваивания";
if (lexeme == ";") return "Разделитель";
if (isalpha(lexeme[0])) return "Идентификатор";
return "Неизвестная лексема";
}
string generateChain(const string& lexeme) {
string chain = "h";
char State = 'h';
for (char ch : lexeme) {
switch (State) {
case 'h':
if (ch == 'i') State = 'A';
else if (ch == 't') State = 'C';
else if (ch == ':') State = 'M';
else if (ch == 'e') State = 'G';
else if (ch == ';') State = 'K';
else if (ch == '>' || ch == '<' || ch == '=') State = 'L';
else if (isdigit(ch)) State = 'O'; // Обработка для чисел
else State = 'i';
break;
case 'A':
if (ch == 'f') State = 'B';
else State = 'i';
break;
case 'C':
if (ch == 'h') State = 'D';
else State = 'i';
break;
case 'M':
if (ch == '=') State = 'N';
else State = 'e';
break;
case 'O':
if (isdigit(ch) || (toupper(ch) >= 'A' && toupper(ch) <= 'F')) State = 'O'; // Обработка для шестнадцатеричных чисел
break;
default:
break;
}
chain += State;
}
chain += "s";
return chain;
}
void reportUnknownLexeme(const string& lexeme, int lineNumber) {
cout << "Неизвестная лексема \"" << lexeme << "\" в строке " << lineNumber << endl;
}
vector<Lexeme> lexicalAnalysis(const string& text) {
vector<Lexeme> lexemes;
string current;
bool inComment = false;
int lineNumber = 0;
for (size_t i = 0; i < text.length(); i++) {
char ch = text[i];
if (ch == '\n') lineNumber++;
if (!inComment && ch == '/' && text[i + 1] == '*') {
inComment = true;
i++;
continue;
}
if (inComment && ch == '*' && text[i + 1] == '/') {
inComment = false;
i++;
continue;
}
if (inComment) continue;
if (isspace(ch) || ch == ';' || ch == '<' || ch == '>' || ch == '=' || ch == ':') {
if (!current.empty()) {
string type = determineLexemeType(current);
if (type == "Неизвестная лексема") {
reportUnknownLexeme(current, lineNumber);
}
else {
lexemes.push_back({ current, type, generateChain(current), lineNumber });
}
current.clear();
}
if (ch == ';' || ch == '<' || ch == '>' || ch == '=') {
string lexemeStr(1, ch);
string type = determineLexemeType(lexemeStr);
if (type == "Неизвестная лексема") {
reportUnknownLexeme(lexemeStr, lineNumber);
}
else {
lexemes.push_back({ lexemeStr, type, generateChain(lexemeStr), lineNumber });
}
}
if (ch == ':' && text[i + 1] == '=') {
lexemes.push_back({ ":=", "Оператор присваивания", generateChain(":="), lineNumber });
i++;
}
}
else {
current += ch;
}
}
if (!current.empty()) {
string type = determineLexemeType(current);
if (type == "Неизвестная лексема") {
reportUnknownLexeme(current, lineNumber);
}
else {
lexemes.push_back({ current, type, generateChain(current), lineNumber });
}
}
return lexemes;
}
bool isValidSequence(const string& previousLexeme, const string& currentLexeme) {
static unordered_map<string, vector<string>> precedenceTable = {
{"if", {"i"}},
{"then", {"i"}},
{"else", {"i", "if"}},
{"i", {"<", ">", "=", "then", ":="}},
{"<", {"i"}},
{">", {"i"}},
{"=", {"i"}},
{":=", {"O"}},
{"O", {";", "else"}}
};
auto it = precedenceTable.find(previousLexeme);
if (it != precedenceTable.end()) {
const auto& allowedNextLexemes = it->second;
return find(allowedNextLexemes.begin(), allowedNextLexemes.end(), currentLexeme) != allowedNextLexemes.end();
}
return false;
}
void syntaxAnalysis(const vector<Lexeme>& lexemes) {
stack<string> automatonStack;
automatonStack.push(".н");
vector<string> lexemeArray;
vector<int> appliedRules;
size_t index = 0;
string previousLexeme = "";
for (const auto& lexeme : lexemes) {
string valueToAdd = lexeme.value;
if (lexeme.type == "Идентификатор") {
valueToAdd = "i";
}
else if (lexeme.type == "Шестнадцатеричное число") {
valueToAdd = "O";
}
lexemeArray.push_back(valueToAdd);
if (!previousLexeme.empty() && !isValidSequence(previousLexeme, valueToAdd)) {
cout << "Синтаксическая ошибка, разбор невозможен: после " << previousLexeme
<< " ожидалась другая лексема." << endl;
return;
}
if (valueToAdd == "i" && previousLexeme == "") {
cout << "Синтаксическая ошибка, разбор невозможен: перед i ожидалась лексема" << endl;
return;
}
previousLexeme = valueToAdd;
}
lexemeArray.push_back(".к");
auto printState = [&](const string& rule, const string& action = "", const vector<int>& rulesApplied = {}) {
cout << "{ ";
for (size_t i = index; i < lexemeArray.size(); ++i) {
cout << lexemeArray[i] << " ";
}
cout << "| ";
stack<string> tempStack = automatonStack;
vector<string> rightStack;
while (!tempStack.empty()) {
rightStack.push_back(tempStack.top());
tempStack.pop();
}
for (auto it = rightStack.rbegin(); it != rightStack.rend(); ++it) {
cout << *it << " ";
}
if (!action.empty()) {
cout << "| " << action;
}
if (!rulesApplied.empty()) {
for (int ruleNum : rulesApplied) {
cout << " " << ruleNum;
}
}
cout << " }" << rule << endl;
};
auto reduce = [&](int ruleNumber, const vector<string>& pattern, const string& replacement) {
stack<string> tempStack;
for (auto it = pattern.rbegin(); it != pattern.rend(); ++it) {
if (!automatonStack.empty() && automatonStack.top() == *it) {
tempStack.push(automatonStack.top());
automatonStack.pop();
}
else {
while (!tempStack.empty()) {
automatonStack.push(tempStack.top());
tempStack.pop();
}
return false;
}
}
automatonStack.push(replacement);
appliedRules.push_back(ruleNumber);
return true;
};
string rules = "";
printState(rules);
while (index < lexemeArray.size()) {
string current = lexemeArray[index];
automatonStack.push(current);
index++;
printState(rules, "%п", appliedRules);
bool reduced;
do {
reduced = false;
vector<int> currentAppliedRules;
if (index < lexemeArray.size() && lexemeArray[index] == "else") {
if (reduce(2, { "if", "E", "then", "F", "else", "F" }, "F")) {
reduced = true;
currentAppliedRules.push_back(2);
printState(rules, "%с", currentAppliedRules);
continue;
}
}
else if (reduce(3, { "if", "E", "then", "F" }, "F")) {
reduced = true;
currentAppliedRules.push_back(3);
printState(rules, "%с", currentAppliedRules);
}
if (reduce(7, { "i", "<", "i" }, "E")) {
reduced = true;
currentAppliedRules.push_back(7);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(8, { "i", ">", "i" }, "E")) {
reduced = true;
currentAppliedRules.push_back(8);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(9, { "i", "=", "i" }, "E")) {
reduced = true;
currentAppliedRules.push_back(9);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(4, { "i", ":=", "O" }, "F")) {
reduced = true;
currentAppliedRules.push_back(4);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(1, { "F", ";" }, "S")) {
reduced = true;
currentAppliedRules.push_back(1);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(2, { "if", "E", "then", "F", "else", "F" }, "F")) {
reduced = true;
currentAppliedRules.push_back(2);
printState(rules, "%с", currentAppliedRules);
}
if (reduced) {
continue;
}
} while (reduced);
}
cout << "Применённые правила: ";
for (int rule : appliedRules) {
cout << rule << " ";
}
cout << endl;
}
void generateTriads(const vector<Lexeme>& lexemes) {
queue<int> actionQueue;
queue<int> thenQueue;
queue<int> conditionQueue;
queue<int> ifQueue;
queue<int> elseQueue;
int lastTriadIndex = -1;
size_t i = 0;
unordered_map<Triad, int> triadIndexMap;
while (i < lexemes.size()) {
const string& lexemeValue = lexemes[i].value;
if (lexemeValue == "<" || lexemeValue == ">" || lexemeValue == "=") {
if (i > 0 && i < lexemes.size()) {
Triad newTriad(lexemeValue, lexemes[i - 1].value, lexemes[i + 1].value);
triads.push_back(newTriad);
conditionQueue.push(++lastTriadIndex);
}
}
i++;
}
i = 0;
while (i < lexemes.size()) {
const string& lexemeValue = lexemes[i].value;
if (lexemeValue == ":=") {
if (i > 0 && i < lexemes.size()) {
Triad newTriad(lexemeValue, lexemes[i - 1].value, lexemes[i + 1].value);
triads.push_back(newTriad);
actionQueue.push(++lastTriadIndex);
}
}
i++;
}
i = 0;
while (i < lexemes.size()) {
const string& lexemeValue = lexemes[i].value;
if (lexemeValue == "then") {
if (!actionQueue.empty()) {
int actionTriadIndex = actionQueue.front();
Triad newTriad("then", "^" + to_string(actionTriadIndex), "");
triads.push_back(newTriad);
thenQueue.push(++lastTriadIndex);
actionQueue.pop();
}
}
i++;
}
i = 0;
while (i < lexemes.size()) {
const string& lexemeValue = lexemes[i].value;
if (lexemeValue == "if") {
if (!conditionQueue.empty() && !thenQueue.empty()) {
int conditionTriad = conditionQueue.front();
conditionQueue.pop();
int thenTriadIndex = thenQueue.front();
thenQueue.pop();
Triad newTriad("if", "^" + to_string(conditionTriad), "^" + to_string(thenTriadIndex));
triads.push_back(newTriad);
ifQueue.push(++lastTriadIndex);
}
}
i++;
}
i = 0;
while (i < lexemes.size()) {
const string& lexemeValue = lexemes[i].value;
if (lexemeValue == "else") {
if (!ifQueue.empty() && elseQueue.empty()) {
int firstIfTriadIndex = ifQueue.front();
ifQueue.pop();
int secondIfTriadIndex = -1;
if (!ifQueue.empty()) {
secondIfTriadIndex = ifQueue.front();
ifQueue.pop();
}
if (secondIfTriadIndex != -1) {
Triad newTriad("else", "^" + to_string(firstIfTriadIndex), "^" + to_string(secondIfTriadIndex));
triads.push_back(newTriad);
}
else if (!actionQueue.empty()) {
int actionTriadIndex = actionQueue.front();
actionQueue.pop();
Triad newTriad("else", "^" + to_string(firstIfTriadIndex), "^" + to_string(actionTriadIndex));
triads.push_back(newTriad);
}
}
else if (!elseQueue.empty() && !ifQueue.empty()) {
int ifTriadIndex = ifQueue.front();
int elseTriadIndex = elseQueue.front();
ifQueue.pop();
elseQueue.pop();
Triad newTriad("else", "^" + to_string(elseTriadIndex), "^" + to_string(ifTriadIndex));
triads.push_back(newTriad);
}
else if (!elseQueue.empty() && !actionQueue.empty()) {
int elseTriadIndex = elseQueue.front();
elseQueue.pop();
int actionTriadIndex = actionQueue.front();
actionQueue.pop();
Triad newTriad("else", "^" + to_string(elseTriadIndex), "^" + to_string(actionTriadIndex));
triads.push_back(newTriad);
}
elseQueue.push(++lastTriadIndex);
}
i++;
}
}
void generateUniqueTriads(const vector<Triad>& triads) {
queue<int> actionUniqueQueue;
queue<int> thenUniqueQueue;
queue<int> conditionUniqueQueue;
queue<int> ifUniqueQueue;
queue<int> elseUniqueQueue;
int lastTriadIndex = -1;
size_t i = 0;
unordered_map<Triad, int, TriadHasher> triadIndexMap;
for (const auto& triad : triads) {
if (triad.operation == "<" || triad.operation == ">" || triad.operation == "=") {
if (triadIndexMap.find(triad) == triadIndexMap.end()) {
triadIndexMap[triad] = ++lastTriadIndex;
uniqueTriads.push_back(triad);
}
conditionUniqueQueue.push(triadIndexMap[triad]);
}
}
for (const auto& triad : triads) {
if (triad.operation == ":=") {
if (triadIndexMap.find(triad) == triadIndexMap.end()) {
triadIndexMap[triad] = ++lastTriadIndex;
uniqueTriads.push_back(triad);
}
actionUniqueQueue.push(triadIndexMap[triad]);
}
}
for (const auto& triad : triads) {
if (triad.operation == "then") {
if (!actionUniqueQueue.empty()) {
int actionIndex = actionUniqueQueue.front();
actionUniqueQueue.pop();
Triad newTriad("then", "^" + to_string(actionIndex), "");
if (triadIndexMap.find(newTriad) == triadIndexMap.end()) {
triadIndexMap[newTriad] = ++lastTriadIndex;
uniqueTriads.push_back(newTriad);
}
thenUniqueQueue.push(triadIndexMap[newTriad]);
}
}
}
for (const auto& triad : triads) {
if (triad.operation == "if") {
if (!conditionUniqueQueue.empty() && !thenUniqueQueue.empty()) {
int conditionIndex = conditionUniqueQueue.front();
conditionUniqueQueue.pop();
int thenIndex = thenUniqueQueue.front();
thenUniqueQueue.pop();
Triad newTriad("if", "^" + to_string(conditionIndex), "^" + to_string(thenIndex));
if (triadIndexMap.find(newTriad) == triadIndexMap.end()) {
triadIndexMap[newTriad] = ++lastTriadIndex;
uniqueTriads.push_back(newTriad);
}
ifUniqueQueue.push(triadIndexMap[newTriad]);
}
}
}
while (i < triads.size()) {
const string& operation = triads[i].operation;
if (operation == "else") {
if (!ifUniqueQueue.empty() && elseUniqueQueue.empty()) {
int firstIfTriadIndex = ifUniqueQueue.front();
ifUniqueQueue.pop();
int secondIfTriadIndex = -1;
if (!ifUniqueQueue.empty()) {
secondIfTriadIndex = ifUniqueQueue.front();
ifUniqueQueue.pop();
}
if (secondIfTriadIndex != -1) {
Triad newTriad("else", "^" + to_string(firstIfTriadIndex), "^" + to_string(secondIfTriadIndex));
if (find(uniqueTriads.begin(), uniqueTriads.end(), newTriad) == uniqueTriads.end()) {
uniqueTriads.push_back(newTriad);
elseUniqueQueue.push(++lastTriadIndex);
}
}
else if (!actionUniqueQueue.empty()) {
int actionTriadIndex = actionUniqueQueue.front();
actionUniqueQueue.pop();
Triad newTriad("else", "^" + to_string(firstIfTriadIndex), "^" + to_string(actionTriadIndex));
if (find(uniqueTriads.begin(), uniqueTriads.end(), newTriad) == uniqueTriads.end()) {
uniqueTriads.push_back(newTriad);
elseUniqueQueue.push(++lastTriadIndex);
}
}
}
else if (!elseUniqueQueue.empty() && !ifUniqueQueue.empty()) {
int ifTriadIndex = ifUniqueQueue.front();
int elseTriadIndex = elseUniqueQueue.front();
ifUniqueQueue.pop();
elseUniqueQueue.pop();
Triad newTriad("else", "^" + to_string(elseTriadIndex), "^" + to_string(ifTriadIndex));
if (find(uniqueTriads.begin(), uniqueTriads.end(), newTriad) == uniqueTriads.end()) {
uniqueTriads.push_back(newTriad);
elseUniqueQueue.push(++lastTriadIndex);
}
}
else if (!elseUniqueQueue.empty() && !actionUniqueQueue.empty()) {