Size: a a a

Compiler Development

2021 January 27

K

Kir in Compiler Development
for(int c; (c = getchar()) != EOF;)
Есть-ли какой-нибудь формат для представления хода выполнения программы? Т.е. все вызовы функций, итерации циклов, проверки условий, присвоения переменных и т.д. Я хочу написать проход для компилятора который к исходному коду добавляет логи - т.е. для кода

for i in 0 .. 3:
 echo i

мы должны получить

for i in 0 .. 3:
 log("loop start")
 log("var set i = ", i)
 log("function call echo", i)
 echo i
 log("loop end")

Проход должен работать на AST после семантической проверки - т.е. есть доступ ко всем символам, определения типов и прочее, так что конкретные детали реализации можно не учитывать (как получать значения переменных, записи в них и т.д)

Основная проблема в формате записи и восстановления хода работы после  выполнения программы - можно придумать какой-то свой формат записи, но может быть уже существуют похожие решения?

И впоследствии я хочу сделать что-то схожее с дебаггером в котором можно проследить весь "путь" значения через все присвоения, вызовы и циклы. Или сделать визуализацию работы самой программы.
strace разве что на ум приходит
источник

f

for(int c; (c = getc... in Compiler Development
Да, я еще нашел rr и ftrace которые показались мне похожими - но опять же - уровень ниже чем мне нужно - только вызовы функций.
источник

VS

Victor Shamparov in Compiler Development
for(int c; (c = getchar()) != EOF;)
Есть-ли какой-нибудь формат для представления хода выполнения программы? Т.е. все вызовы функций, итерации циклов, проверки условий, присвоения переменных и т.д. Я хочу написать проход для компилятора который к исходному коду добавляет логи - т.е. для кода

for i in 0 .. 3:
 echo i

мы должны получить

for i in 0 .. 3:
 log("loop start")
 log("var set i = ", i)
 log("function call echo", i)
 echo i
 log("loop end")

Проход должен работать на AST после семантической проверки - т.е. есть доступ ко всем символам, определения типов и прочее, так что конкретные детали реализации можно не учитывать (как получать значения переменных, записи в них и т.д)

Основная проблема в формате записи и восстановления хода работы после  выполнения программы - можно придумать какой-то свой формат записи, но может быть уже существуют похожие решения?

И впоследствии я хочу сделать что-то схожее с дебаггером в котором можно проследить весь "путь" значения через все присвоения, вызовы и циклы. Или сделать визуализацию работы самой программы.
Control Flow Graph с каким-нибудь присоединённым IR.
UPD. Кажется, я не так понял вопрос.
источник

f

for(int c; (c = getc... in Compiler Development
Что-то сходное с CFG да. Хотя скорее всего более конкретного решения по эту задачу все равно нет, так как я не смог найти ничего похожего кроме просто банальных блок-схем.
источник

PS

Peter Sovietov in Compiler Development
for(int c; (c = getchar()) != EOF;)
Что-то сходное с CFG да. Хотя скорее всего более конкретного решения по эту задачу все равно нет, так как я не смог найти ничего похожего кроме просто банальных блок-схем.
Если речь об исполнении, то не CFG, DFG — DAG, в котором узлы это операции, а ребра — динамические зависимости по данным. Такое существует и используется для визуализации трасс исполнения. Для весьма специальных применений.
источник

VS

Victor Shamparov in Compiler Development
Peter Sovietov
Если речь об исполнении, то не CFG, DFG — DAG, в котором узлы это операции, а ребра — динамические зависимости по данным. Такое существует и используется для визуализации трасс исполнения. Для весьма специальных применений.
Ну я бы сказал так:
1. Для всяких call, переходов после if, итераций циклов и подобного (часть указанного в оригинальном вопросе) имхо удобнее использовать CFG.
2. Для распространения данных действительно лучше DFG-DAG.
источник

f

for(int c; (c = getc... in Compiler Development
Peter Sovietov
Если речь об исполнении, то не CFG, DFG — DAG, в котором узлы это операции, а ребра — динамические зависимости по данным. Такое существует и используется для визуализации трасс исполнения. Для весьма специальных применений.
DAG, а скорее всего просто дерево без каких-либо общих узлов скорее всего это ближе всего к тому что мне нужно.

То есть для кода должен получится примерно такой граф.

for i in 0 .. 3:
 echo i + i

пунктирной линией показан переход управления на уровне выше (относительно выполнения тела функции)
источник
2021 January 28

DP

Dmitry Ponyatov in Compiler Development
for(int c; (c = getchar()) != EOF;)
Есть-ли какой-нибудь формат для представления хода выполнения программы? Т.е. все вызовы функций, итерации циклов, проверки условий, присвоения переменных и т.д. Я хочу написать проход для компилятора который к исходному коду добавляет логи - т.е. для кода

for i in 0 .. 3:
 echo i

мы должны получить

for i in 0 .. 3:
 log("loop start")
 log("var set i = ", i)
 log("function call echo", i)
 echo i
 log("loop end")

Проход должен работать на AST после семантической проверки - т.е. есть доступ ко всем символам, определения типов и прочее, так что конкретные детали реализации можно не учитывать (как получать значения переменных, записи в них и т.д)

Основная проблема в формате записи и восстановления хода работы после  выполнения программы - можно придумать какой-то свой формат записи, но может быть уже существуют похожие решения?

И впоследствии я хочу сделать что-то схожее с дебаггером в котором можно проследить весь "путь" значения через все присвоения, вызовы и циклы. Или сделать визуализацию работы самой программы.
нужен ascii файловый формат, или достаточно сериализации структуры данных?
как готовые примеры, смотрите формат сериализации jetbrain mps , и clojure x-expressions (list+map).
в общем случае — стройте объектный граф в памяти, и реализуйте для него персистентность и structural pattern matching (unification)
источник

DP

Dmitry Ponyatov in Compiler Development
for(int c; (c = getchar()) != EOF;)
Есть-ли какой-нибудь формат для представления хода выполнения программы? Т.е. все вызовы функций, итерации циклов, проверки условий, присвоения переменных и т.д. Я хочу написать проход для компилятора который к исходному коду добавляет логи - т.е. для кода

for i in 0 .. 3:
 echo i

мы должны получить

for i in 0 .. 3:
 log("loop start")
 log("var set i = ", i)
 log("function call echo", i)
 echo i
 log("loop end")

Проход должен работать на AST после семантической проверки - т.е. есть доступ ко всем символам, определения типов и прочее, так что конкретные детали реализации можно не учитывать (как получать значения переменных, записи в них и т.д)

Основная проблема в формате записи и восстановления хода работы после  выполнения программы - можно придумать какой-то свой формат записи, но может быть уже существуют похожие решения?

И впоследствии я хочу сделать что-то схожее с дебаггером в котором можно проследить весь "путь" значения через все присвоения, вызовы и циклы. Или сделать визуализацию работы самой программы.
а вот про систему сквозного логирования программы и анализа трейсов очень бы хотелось самому узнать
источник

ED

Edmond Dantes in Compiler Development
Доброе утро. С точки зрения структуры FlowControl какой термин используется для операторов: call\return\yield
Они изменяют состояние стека. Есть ли особый термин, чтобы пометить такие операторы в теории?
И второй вопрос. Блоки try\catch\finally как я понимаю, не должны относится к этой группе?
источник

JT

James Tevision in Compiler Development
Делаю таблицу символов

Функции для работы с ней
SymTable_insert() - вставляет новый символ
SymTable_find() - находит символ илт NULL
SymTable_delLastN() - удаляет последние N символов (для закрытия области видимости)

И теперь вопрос:
Как правильно хранить N (кол-во симаолов в новом скоупе)

Либо хранить только последнее N и находя новый лекс блок рекурсивно работать с ним (так должно быть легче работать с парсером когда он собирает stmt_block, но дороже из-за рекурсии)
Либо хранить массив N и удалять/добавлять значения в нем (дороже по памяти)

Что можете подсказать по этому поводу?
источник

AT

Alexander Tchitchigi... in Compiler Development
James Tevision
Делаю таблицу символов

Функции для работы с ней
SymTable_insert() - вставляет новый символ
SymTable_find() - находит символ илт NULL
SymTable_delLastN() - удаляет последние N символов (для закрытия области видимости)

И теперь вопрос:
Как правильно хранить N (кол-во симаолов в новом скоупе)

Либо хранить только последнее N и находя новый лекс блок рекурсивно работать с ним (так должно быть легче работать с парсером когда он собирает stmt_block, но дороже из-за рекурсии)
Либо хранить массив N и удалять/добавлять значения в нем (дороже по памяти)

Что можете подсказать по этому поводу?
Сделайте просто linked list из таблиц по одной на каждый скоуп.
источник

JT

James Tevision in Compiler Development
Alexander Tchitchigin
Сделайте просто linked list из таблиц по одной на каждый скоуп.
Но тогда они не будут "наследоваться" ведь

{
     /* scope1 */
     {
          /* scope2 */
      }
}
Из scope2 можно обратиться к переменным scope1
И в текущем варианте такой  crossScopeSearch (выдуманый термин)
Относительно быстрый

А если у меня будет список (не обязателельно связный) таблиц
Мне прийдется проводить поиск в каждой из них
источник

AT

Alexander Tchitchigi... in Compiler Development
James Tevision
Но тогда они не будут "наследоваться" ведь

{
     /* scope1 */
     {
          /* scope2 */
      }
}
Из scope2 можно обратиться к переменным scope1
И в текущем варианте такой  crossScopeSearch (выдуманый термин)
Относительно быстрый

А если у меня будет список (не обязателельно связный) таблиц
Мне прийдется проводить поиск в каждой из них
Слышали насчёт "premature optimization"? 😉
источник

JT

James Tevision in Compiler Development
Alexander Tchitchigin
Слышали насчёт "premature optimization"? 😉
Ну... да, возможно это преждевременно, но тогда уж легче хранить список длин скоупов


И если есть реализация позволяющая быстро закрывать N последних
Почему бы ей не пользоваться?
источник

AT

Alexander Tchitchigi... in Compiler Development
James Tevision
Ну... да, возможно это преждевременно, но тогда уж легче хранить список длин скоупов


И если есть реализация позволяющая быстро закрывать N последних
Почему бы ей не пользоваться?
А какая у Вас тогда асимптотика вставки и поиска?
источник

JT

James Tevision in Compiler Development
Alexander Tchitchigin
А какая у Вас тогда асимптотика вставки и поиска?
Вставка и поиск идут по бинарному дереву
(Не сбалансированому) но имена перемнных вроде относительно "случайные" так что сильного вырождения быть не должно
источник

JT

James Tevision in Compiler Development
Ну либо в программе переменные в алфавитном порядке
источник

AT

Alexander Tchitchigi... in Compiler Development
James Tevision
Вставка и поиск идут по бинарному дереву
(Не сбалансированому) но имена перемнных вроде относительно "случайные" так что сильного вырождения быть не должно
Тогда откуда "реализация позволяющая быстро закрывать N последних"? Либо что значит "быстро"?
источник

JT

James Tevision in Compiler Development
Alexander Tchitchigin
Тогда откуда "реализация позволяющая быстро закрывать N последних"? Либо что значит "быстро"?
Я сделал структуру связывающую бин-дерево и стек
Поэтому могу удалить последний элемент за O(1)
источник