Nitra: Backend
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.01.17 11:32
Оценка: 24 (1)
#Имя: wiki.Nitra.Backend
Здравствуйте, fddima, Вы писали:

F>В плане — ты упомянул бэкэнд. Это в принципе наверное то, что ты имел ввиду в этой ветке
Автор: fddima
Дата: 19.01.17
, когда я говорил, что как жаль, что нет возможности делать нэйтив.


F>Я прежде всего хотел бы понять твои личные (они будут ближе всего к правде) — сколько тут работы.


F>...Т.е.:

F>а) мы генерим нативный парсер
F>б) мы генерим всю нитру нативную
F>в) ...
F>?!

Прежде всего надо понять одну простую вещь. Nemerle и Nitra написаны на Nemerle путем бутстрапинга (компиляции на своих предыдущих версиях с постоянным обновлением оных). Значит сделав кросплатформный генератор кода для Nemerle мы получим возможность перенести и Nemerle, и Nitra на эту самую платформу.

Для понимания того что такое бэкэнд и как он позволяет абстрагировать компиляторы от платформы опишу процесс компиляции на очень абстрактном уровне.

Описание стадий компиляции


Итак, компиляция состоит из следующих этапов:
1. Компилятор получает список исходников и список библиотек (сборок дотнета, dll или so, в зависимости от платформы).

2. Компилятор считывает из библиотек метаинформацию. В основном это описание типов. Для доисторических языков типа С++/С этот не нужно, так как метаинформация в них получается из заголовочных файлов. Но мы говорим о современном языке (Nemerle) который не может жить без метаинформации.

Метаинформация описывается в универсальном формате символов, предоставляемом Nitra.

Для платформ типа .Net или Java символы имеющие аналоги в эти платформах должны считываться и записываться в формате этой платформой. Это приводит к тому, что для платформ со стандартизированными метаданными нужно писать код декодирования и кодирования (записи в формате платформы) символов Nitra из/в этих форматов.

Nitra предоставляет библиотечные функции записи и считывания графа символов из потока ввода/вывода. Если для платформы нет стандарта метаданных, можно просто пользоваться функционалом Nitra-ы по сериализации и десериализации метаданных. Таким образом, для нэйтива эту часть фактически даже не надо реализовывать.

3. Компилятор парсит файлы и преобразует их в AST.

4. Компилятор анализирует AST и создает набор символов описывающих именованные сущности компилируемой проеграммы.

5. Компилятор анализирует AST и связывает ссылки на имена с символами и осуществляет проверки корректности.
В результате этого этапа программа описывается как граф символов и AST. При этом AST становится аннотирован информацией четко детерминирующей значение каждой его подветки. По такому представлению уже можно генерировать код.

6. Компилятор производит трансформацию полученного AST и символов с целью упрощения генерации кода. На этом этапе высокоуровневые конструкции вроде замыканий и функциональных типов могут быть преобразованы в классы и ссылки на объекты, если в платформе для которой будет генерироваться код не поддерживаются эти высокоуровневые констуркции. В результате этой стадии получается AST и набор символов по которым можно непосредственно генерировать типы для выбранной платформы.

7. Символы описанные в программе (вычисленные на предыдущих стадиях) сериализуются в формат платформы. Для платформ имеющих свой формат метаданных вызвается специально написанная для них функция мериализации метаданных. Она, используя некий API специфичный для платформы, записывает метаданные.

Если платформа не поддерживает метаданных, они могут быть сохранены универсальным образом в поток, а поток может быть записан в компилируемый моудуль (exe, dll, so, ...).

8. Производится генерация кода для выбранной платформы. Этот процесс опять таки можно представить как функцию. На вход ей даются корневые символы полученные на стадии 6 и предыдущих ей. Эта функция обходит все вложенные символы и AST на который они ссылаются (в основном AST кода расположенного внутри функций) и путем вызова API платформы генерирует исполняемый код. Если речь идет о совсем нэйтиве без использования промежуточных форматов вроде LLVM или кода c, то генерируется самопальный промежуточный троичный код (как это описано в учебниках по компиляторам), а уже по нему ассемблерные инструкции. На практики этого делать не надо, так как есть LLVM и тот же С.

Backend


Думаю, что из описанных шагов ясно, что к Backend-у относятся пункты: 2, 7, 8 и (частично) 6.

Таким образом бэкэнд должен уметь:
1. Считать метаданные из библиотек и преобразовать ее в граф символов Nitra.

Символы Nitra — это нечто вроде классов, но более специализированные. Они описываются в виде декларций в файлах .nitra. Вот пример такой декларации для функции в Mini C.

2. Записать граф символов (полученный в результате компиляции проекта) в формат платформы (если надо).

3. Преобразовать отсутствующие в платформе символы и AST в имеющие прямое отображение в платформе AST и символы. Например, преобразовать функциональные типы в ссылки на функции или указатели на объекты, а замыкания в классы или структуры.

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

Пункт 1, из этого списка, нужен как в компиляторе, так и в движке IDE. Остальные пункты нужны только в компиляторах. Таким образом целесообразно разбить бэкнэд на два модуля:
1. Читалка метаданны.
2. Писалка метаданных и кода.

Для поддержки IDE и прочего тулинга достаточно первого модуля (которые, существенно проще реализовать).

Backend для нэйтив


Создавать бэкэнд для нэйтива непосредственно, практически, не имеет смысла. Это большой труд, необходимость знания процессорной архитектуры и привязка к оной.

Более перспективными являются два пути:
1. Генерация кода на переносимом высокоуровневом языка, которым, естественно является С.
2. Использование LLVM как промежуточной высокопереносимой платформы.

Преимущество второго подхода:
1. Отсутствие промежуточной компиляции (из С в нэйтив). Это быстрее.
2. Возможность использования разных оптимизаций.
3. Возможность использования расшерений упрощающих генерации кода для разных фич. Например, я слышал, что в LLVM имеется поддержка замыканий. Так же в LLVM имеется какая-то поддержка GC. Примечание: Не стоит путать наличие поддержки GC и наличие самого GC. GC (вроде как) в LLVM нет. Нужно использовать внешний GC. Но даже наличие поддержки уже упрощает использование внешнего GC.

Портирование Nitra и Nemerle на другие платыормы


Собственно создание бэкэнда еще не является портированием Nitra и Nemerle на платформу поддерживаемую этим бэкэндом.

Наличие бэкэнда для платформы позволяет производить кроскомпиляцию. Так если у нас есть работающие Nitra и Nemerle на донтете, добавив бэкэнд для LLVM мы сможем скомпилировать программу на Nemerle для палтформы поддерживаемой LLVM.

Тот фатк, что сами Nitra и Nemerle являются программами на Nitra и Nemerle позволяет нам скомпилировать Nitra и Nemerle под нужнаю нам нэйтив-платформу.

При этом мы получим Nitra и Nemerle, которые могут производить компиляцию как для этой же (т.е. нэйтив) платформы, так и для всех других (если их АПИ могут работать в отсуствии самой платформы или платформа доступна на данной ОС). В общем, кроскомпиляция работает во все стороны.

Поддержка GC


Ты правильно заметил, что Nemerle, а стало быть и Nitra нуждаются в GC, и что на целевой платформе придется как-то воспроизвести поддержку GC.

Тут есть следующие соображения. Для Nitra наличие GC необходимо, но в силу природы работы Nitra этот GC может быть очень примитивный (по крайней мере на первых порах). Фактически можно свести поддержку GC к реализации пула в котором память выделяется последовательно, а освобождается залпом в одной (или двух-трех) безопасной точке. Это приведет к небольшому перерасходу памяти, но при современных ее объемах это не критично.

Фактически компиляция в интре — это эдакая последовательность действий (пайп). В конце этой последовательности (после записи сборки на диск) всю память можно освободить одним залпом.

Ну, а языки создаваемые на Nitra могут и вовсе не поддерживать GC. Так что можно создать версию того же Nemerle на подсчете ссылок, и со стековой RAII или их смеси. Ну, а потом уже можно будет докрутить полноценный GC. В этой области есть масса идей.


26.01.17 14:33: Ветка выделена из темы [Nitra] Backend
Автор: VladD2
Дата: 21.01.17
— VladD2
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 26.01.2017 11:34 VladD2 . Предыдущая версия .
Re: Nitra: Backend
От: s22  
Дата: 26.01.17 13:11
Оценка: +1
Мое мнение не претендующее на правильность и гениальность:

1. перед LLVM лучше генерировать промежуточный язык из которого генерировать уже LLVM.
проще отладка + оптимизации к такому пришли в RUST.

2. Ура, Влад понял что GC не всегда нужна.

3. в С++17 могут включить модули и тогда надо будет читать бинарные метаданные.\

4. И Влад, первый бакэнд сделай даже не под НЕТ, а под джавускрипт.
Отредактировано 27.01.2017 11:11 s22 . Предыдущая версия .
Re: Nitra: Backend
От: Mystic Artifact  
Дата: 22.04.17 21:23
Оценка:
Здравствуйте, VladD2, Вы писали:

Влад, это я был вопрошающим, сменил ник просто (10 лет назад хотел избавиться от старого но лень было).

Спасибо тебе огромное, за столь подробное описание. Я почти сразу это прочитал, но тогда не решился комментировать. Теперь решился.

К сожалению, я, как и писал — в идле. От меня ждать реально тут нельзя ничего. Жизнь меняется, ребёнок не так давно появился. В общем не до глупостей.

Я считаю — что развивать бэкэнды для Нитры — важно. К сожалению, как я и предполагал, тут огромный фронт работы. В том контексте, что спрашивал я — наверное быстрее осилят нормальный .net native. Но это не значит, что бэком не надо заниматься.

Я тут вижу годы работы или год работы опытной команды. У меня в последнее время сложилось впечатление что опытных (умеющих быстро учится) людей как раз не много. Ну а людей типа меня с бэкграундом артистического программинга на асме в молодости — и того меньше. Не-не. Я тупой как пробка. Просто люди без этого бэка и вовсе не могут читать любой IL/IR. Короче.

Задача потребует специфичных знаний и немалой крови.

Я сейчас не могу на это пойти. Я и так перманентно занят. Плюс у меня уже несколько лет идёт лениво хобби проект, который язык. По факту — на сегодня — я разочаровался в своих прежних идеях.

В общем — отдать свои годы — Нитре — я не готов. Работать надо серьёзно. Фан? У меня уже есть фан. Поэтому сорри. Хотя я очень благосклонно отношусь. Если работа на фултайм — эт совершенно иное. Ну ты сам знаешь.

В общем:
1. Имхо пока это не целесообразно.
2. Тулинг на дотнете может жить. Все кого это не устраивает — обойдутся.
3. Есть имхо более перспективные напрвления, скажем щас есь поддержка VS. Может после нетстандарт2 надо на него затачится и скажем задружится с иклипсами какими-нибудь. В любом случае это перспективней, чем другой бэкэнд.
4. Как я уже писал — как только дотнеткора осилит AOT — в обшем-то будет то что нужно и так.

Тем не менее, ещё раз выражаю свою благодарность за подробные объяснения. Вот что значит — человек волочёт в проекте!

PS: В свете твоего объяснения — я свои хотелочки реально придержу.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.