High-level Overview of the Native-Code Generation Process

Representation of Pointers

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

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

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

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

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

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

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

Representation of Intermediate Values

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

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

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

В отличие от этого, update_julia_type изменит cgval.typ на typ, только если это можно сделать без затрат (т.е. без генерации какого-либо кода).

Union representation

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

Примитивные процедуры, которые должны уметь обрабатывать помеченные объединения, это:

  • mark-type
  • load-local
  • store-local
  • isa
  • является
  • emit_typeof
  • emit_sizeof
  • заблокированный
  • разобрать
  • специализированный 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. Он может быть недостаточно большим, чтобы содержать объединение всех типов, которые могут быть там сохранены в соответствии с соответствующим полем типа Union. Используйте соответствующие меры предосторожности при копировании.

Specialized Calling Convention Signature Representation

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

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

Общие принципы заключаются в том, что:

  • Примитивные типы передаются в регистрах int/float.
  • Кортежи типов VecElement передаются в векторных регистрах.
  • Структуры передаются по стеку.
  • Возвращаемые значения обрабатываются аналогично аргументам, с порогом размера, при котором они будут возвращены через скрытый аргумент sret.

Общая логика для этого реализована в get_specsig_function и deserves_sret.

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