Реализация JIT
В состав языка взодят следующие компоненты:
- Приложение компилятор и REPL одновременнно nlc.
- Библиотека nlc-rt - минимально необходимые компоненты для работы с обощенными типами данных.
- Рантайм компоненты проекта libtorch - для тензорных вычислений.
- Библиотека nlc-jit - реализация REPL и JIT и нативного компилятора
- Рантайм библиотека LLVM как зависимость для nlc-jit.
Варианты сборки приложения и необходимые для его работы библиотеки зависят от следующих факторов:
?????????????1. Если для работы приложение не требуется JIT компилятор и динамическая загрузка модулей, тогда приложение можно собрать в нативную программу как обычное приложение на C/C++.
Используются ключи –no-nlc-jit и –nlc-embed-source
Созадется один чистый исполняемый файл без связи с библиотеками nlc-jit и nlc-rt и без прочих зависимостей. Программа должна состоять из одного единственного встроенного кода C/C++ и внутри него не должно быть обращения к объектам языка или аргументам.
?????????????2. Если приложение кроме нативных объектов использует другие типы данных, например тензоры или рациональные числа неограниченной точности и/или создаются функции, тогда для работы приложения требуется библиотека nlc-rt и компоненты проекта torch.
Созадется один исполняемый файл без связи с библиотеками nlc-jit. Допускается обработка любых объектов языка, но без возможности динамической загрузки модулей или любой другой рантайм компиляции (REPL, динамической отладки и т.д.).
Во всех остальных случаях используются обе библиотеки (плюс LLVM) и имеется возможность динамиечкой загрузки модулей, REPL и прочие возможностями JIT компиляции, такими, как вставки нативного кода и интерпретация кода в рантайме.
Пример чистого нативного приложения из-за использования одного единственного блока нативного кода и отсутствию отбращения к объектам языка.
./nlc --compile --no-nlc-rt --nlc-embed-source -o hello hello_embed.src
–ofile-cpp –ofile-include –ofile-bytecode –ofile-pre
Пример чистого нативного приложения из-за использования одного единственного блока нативного кода и отсутствию отбращения к объектам языка (в том числе и аргументам). Тело программы (одинственный фрагмент) вставляется в main непосредственно.
# For compile run: ./nlc --compile --no-nlc-rt --embed-source -o hello
# int main(int argc, char* argv[], char* envp[]);
{%
/*
* @\\stdio.h --> #include <stdio.h>
* @\sys\time.h --> #include "sys/time.h"
*/
printf("Hello, world!\n");
%}
Пример простого приложения с использованием только одой библиотеки nlc-rt:
print("Hello, world!\n");
Пример простого приложения с использованием только одой библиотеки nlc_runtime и
интеграцией объектов языка в нативнй код. Тело программы (блок кода) оформляется в виде
функции и вызывается из main с передачей агрументов, переданных программе.
./nlc --compile -o factorial
$top := _;
@try {
$top = :Int32($1);
} @catch(...){
}
if(@latter ~ :Error || $top < 1){
print("Use: factorial <int>\n");
@exit(1);
}
$fact := 1\1; # Rational number 1 (no precision limit)
# Example build programm for factorial calculate
{%
for(int i=2; i <= static_cast<int>($top); i++){
$fact *= i;
}
%}
print("Factorial %d - %s\n", $top, :StrChar(fact));
@exit(0);
try {
} catch (IntAny ret) {
m_latter = ret.m_ret_value;
}
if(m_latter->op_class_test(:Error) || (*top) < 1 ){
print("Use: factorial <int>\n");
throw IntMinus(Obj::CreateValue(1));
}
// {%
for(int i=2; i <= static_cast<int>((*top)); i++){
(*fact) *= i;
}
// %}
nlc -target ??? -compile
compile(source, filename, mode, flag, dont_inherit, optimize)
Параметры:
source - обязательный параметр. Может быть обычной строкой, байтовой строкой, либо объектом абстрактного синтаксического дерева. filename - обязательный параметр. Имя файла, из которого будет читается код. Если код не будет считан из файла, вы можете написать в качестве параметра любую строку <string>. mode - обязательный параметр. Может принимать 3 значения: eval, если источником является одно выражение; exec,если источником является блок операторов; single, если код состоит из одного оператора; flag - необязательный параметр. По умолчанию flags=0 и определяет, будет ли скомпилированный код содержать асинхронные операции или какие инструкции из __future__ следует использовать. Указывается битами. Если нужно задать несколько инструкций, то их можно указывать через or; dont_inherit - необязательный параметр. По умолчанию dont_inherit=False. Указывает, следует ли использовать future определенные в коде, в дополнение в тем, что указаны во flags; optimize - необязательный параметр. По умолчанию optimize=-1. Задаёт уровень оптимизации компилятора: -1 — использовать настройки интерпретатора (регулируются опцией -O); 0 — не оптимизировать, включить debug; 1 — убрать инструкции asserts, выключить debug; 2 — то, что делает 1 + убрать строки документации.