Реализация JIT

В состав языка взодят следующие компоненты:

  1. Приложение компилятор и REPL одновременнно nlc.
  2. Библиотека nlc-rt - минимально необходимые компоненты для работы с обощенными типами данных.
  3. Рантайм компоненты проекта libtorch - для тензорных вычислений.
  4. Библиотека nlc-jit - реализация REPL и JIT и нативного компилятора
  5. Рантайм библиотека 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 + убрать строки документации.