Документация Engee

Общий обзор процесса генерации машинного кода

Представление указателей

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

В противном случае они будут выводиться как литеральные константы.

Чтобы вывести один из этих объектов, вызовите функцию literal_pointer_val. Она будет отслеживать значение Julia и глобального объекта LLVM, гарантируя, что они допустимы как для текущей среды выполнения, так и после десериализации.

При передаче в файл объекта эти глобальные объекты хранятся в виде ссылок в большой таблице gvals. В этом случае десериализатор может ссылаться на них по индексу и реализовать для их восстановления пользовательский ручной механизм, подобный глобальной таблице смещения (GOT).

Указатели функций обрабатываются аналогичным образом. Они хранятся как значения в большой таблице fvals. Как и в случае с глобальными объектами, десериализатор может ссылаться на них по индексу.

Обратите внимание, что функции extern обрабатываются отдельно, с именами, с помощью обычного механизма разрешения символов в компоновщике.

Учтите, что функции ccall также обрабатываются отдельно с помощью используемой вручную таблицы GOT и таблицы компоновки процедур (PLT).

Представление промежуточных значений

Значения передаются в структуре jl_cgval_t. Она представляет R-значение и содержит достаточно информации, чтобы определить, как его присвоить или передать его куда-либо.

Значения создаются с помощью одного из вспомогательных конструкторов, обычно следующего: mark_julia_type (для непосредственных значений) и mark_julia_slot (для указателей на значения).

Функция convert_julia_type может выполнять преобразование между любыми двумя типами. Она возвращает R-значение с cgval.typ, имеющим значение typ. Она приведет объект к требуемому представлению, создавая указатели кучи, выделяя копии стека и вычисляя размеченные объединения по мере необходимости для изменения представления.

Напротив, функция update_julia_type изменит cgval.typ на typ, только если это можно сделать с нулевыми затратами (т. е. не создавая кода).

Представление объединения

Выводимые типы объединений могут быть выделены в стеке через представление размеченного типа.

Для обработки размеченных объединений используются следующие простые процедуры.

  • mark-type

  • load-local

  • store-local

  • isa

  • is

  • emit_typeof

  • emit_sizeof

  • boxed

  • unbox

  • specialized cc-ret

Для всего остального должна существовать возможность обработки в выводе с использованием этих примитивов для реализации разделения объединения.

Представление размеченного объединения в виде пары < void* union, byte selector >. Селектор имеет фиксированный размер byte & 0x7f и разметит объединение первых 126 типов isbits. Он записывает количество в глубину на основе единицы в объединение типов объектов isbits. Индекс, равный нулю, указывает, что union* на самом деле является размеченным выделенным в куче jl_value_t* и должен рассматриваться как обычный упакованный объект, а не как размеченное объединение.

Старший бит селектора (byte & 0x80) можно проверить, чтобы определить, является ли void* на самом деле указателем, выделенным в куче (jl_value_t*), что позволяет избежать затрат на повторное выделение блока, сохраняя при этом возможность эффективной обработки разделения объединения на основе младших битов.

Гарантируется, что byte & 0x7f является точным тестом для типа; если значение может быть представлено меткой, оно никогда не будет помечено byte = 0x80. При тестировании isa нет необходимости также проверять метку типа.

Выделенная область памяти union* может иметь любой размер. Единственным ограничением является то, что она должна быть достаточно большой, чтобы содержать данные, указываемый в данный момент селектором (selector). Она может быть недостаточно большой, чтобы вмещать объединение всех типов, которые могут храниться в нем в соответствии со связанным полем типа объединения. Копирование следует выполнять с осторожностью.

Представление специализированной сигнатуры соглашения о вызовах

Объект jl_returninfo_t описывает детали соглашения о вызове любого вызываемого объекта.

Если какой-либо из аргументов или возвращаемый тип метода может быть представлен в раскрытом виде и метод не использует переменное количество аргументов, ему будет предоставлена оптимизированная сигнатура соглашения о вызове на основе его полей specTypes и rettype.

Общие принципы заключаются в следующем.

  • Примитивные типы передаются в регистрах целых чисел или чисел с плавающей запятой.

  • Типы VecElement передаются в векторных регистрах.

  • Структуры передаются в стеке.

  • Возвращаемые значения обрабатываются аналогично аргументам, с ограничением размера, при котором они будут возвращены с помощью скрытого аргумента sret.

Общая логика реализуется с помощью get_specsig_function и deserves_sret.

Кроме того, если возвращаемый тип является объединением, он может быть возвращен как пара значений (указатель и метка). Если значения объединения могут быть размещены в стеке, достаточное пространство для их хранения также будет передано в качестве скрытого первого аргумента. На что будет указывать возвращаемый указатель — на это пространство, упакованный объект или даже на другую постоянную память, зависит от вызываемого объекта.