diff --git a/docs/einfo.rst b/docs/einfo.rst new file mode 100644 index 0000000..d34ff2a --- /dev/null +++ b/docs/einfo.rst @@ -0,0 +1,356 @@ +====================================================================== +Дополнительные данные (Extended INFOrmation) в представлении lccrt-IR. +====================================================================== + +Основные (пользовательские) типы данных в библиотеке liblccrt. +-------------------------------------------------------------- + +* Тип **lccrt_einfo_category_t** (**lccrt_eic_t**) - идентификатор категории. + + Категория - это любое строковое название, зарегистрированное в модуле (в качестве + категории). Основное назначение - привязка дополнительных данных к элементу + промежуточного представления, то есть к *модулю*, *глобалу*, *функции* и + *операции*. Например, можно использовать название *"profile"* для назначения + *операциям* профильных данных. + + После регистрации категории с новым названием возвращается ее идентификатор. С помощью + идентификатора можно назначить или прочитать ссылку на элемент дополнительных данных (ЭДД). + При этом дополнительные данные сами по себе технически не связаны с категориями. + С технической точки зрения категории используются только для установки/чтения ссылок. + + Одновременно элементу промежуточного представления (*модуль*/*глобал*/*функция*/*операция*) + могут проставляться ссылки для разных категорий, но для каждой категории назначается ровно + одна ссылка. При установке новой ссылки старое значение ссылки на дополнительные данные + затирается. Поскольку ссылка может указывать на структуру дополнительных данных, то в рамках + одной категории для элемента промежуточного представления возможно тем самым указание + произвольного набора данных. + + После регистрации для любой категории ее идентификатор можно также узнать (найти) по + исходному строковому названию. В целом использование идентификаторов вместо исходных + строковых названий связано с заменой сравнения строк на сравнение целочисленных значений, + то есть с оптимизацией операций сравнения в процессе обхода ссылок на дополнительные + данные. + + Интерфейсы: + :: + + extern lccrt_eic_t lccrt_module_new_einfo_category( lccrt_m_ptr m, const char *name); + extern lccrt_eic_t lccrt_module_find_einfo_category( lccrt_m_ptr m, const char *name); + extern lccrt_eir_t lccrt_module_get_einfo( lccrt_m_ptr m, lccrt_eic_t eic); + extern void lccrt_module_set_einfo( lccrt_m_ptr m, lccrt_eic_t eic, lccrt_eir_t value); + extern lccrt_eir_t lccrt_var_get_einfo( lccrt_v_ptr v, lccrt_eic_t eic); + extern void lccrt_var_set_einfo( lccrt_v_ptr v, lccrt_eic_t eic, lccrt_eir_t value); + extern lccrt_eir_t lccrt_function_get_einfo( lccrt_f_ptr func, lccrt_eic_t eic); + extern void lccrt_function_set_einfo( lccrt_f_ptr func, lccrt_eic_t eic, lccrt_eir_t value); + extern lccrt_eir_t lccrt_oper_get_einfo( lccrt_o_ptr oper, lccrt_eic_t eic); + extern void lccrt_oper_set_einfo( lccrt_o_ptr oper, lccrt_eic_t eic, lccrt_eir_t value); + +* Тип **lccrt_einfo_reference_t** (**lccrt_eir_t**) - дескриптор элемента дополнительных данных. + + При создании элемента дополнительных данных (ЭДД) возвращается дескриптор. В дескрипторе + хранится тип данных, а также собственно значение (для простых типов), либо ссылка на значение + (для агрегатных типов). Дескриптор элемента дополнительных данных может затем использоваться + многократно, в частности, при простановке ссылок на ЭДД в рамках различных (!) категорий для + элементов промежуточного представления. Также дескрипторы могут быть значениями в массивах и + структурах (см. ниже). + + Типы значений в дескрипторе: + + * **EMPTY** - значение дескриптора не устанавливалось + + * **INT64** - целочисленное значение (*хранится напрямую в дескрипторе*) + + * **RAW** - байтовый массив фиксированной длины (*ссылка*) + + * **ARRAY** - массив дескрипторов (*ссылка*) + + * **STRUCT** - структура именнованных полей со значениями-дескрипторами (*ссылка*) + + Байтовый RAW-массив создается с фиксированной длиной и сразу с инциализацией. Основное + назначение RAW-массива - это хранение строк. + + ARRAY-массив дескрипторов создается как массив с начальной длиной. При этом возможно + добавление новых элементов в конец массива, с увеличением длины массива. При + необходимости размер фактически выделенной памяти автоматически увеличивается. + Уменьшение размера массива не предусмотрено. Также при создании ARRAY-массива необходимо + указать описание типа элемента (**lccrt_einfo_tydescr_ptr**). + + Про данные типа STRUCT описание приводится ниже (см. **lccrt_einfo_tydescr_ptr**). + + Поскольку ARRAY-массивы и STRUCT-данные могут хранить дескрипторы дополнительных данных, + то возможно построение графа дополнительных данных. Чтобы дополнительные данные были + доступны при последующих обходах, должен иметься путь в графе данных, начинающийся с + элемента промежуточного представления. Ссылка на данные в элементе промежуточного + представления доступна через категорию. Тем самым дополнительные данные стоит создавать + и рассматривать как независимые графы, с логической точки зрения разбитые на категории. + Например, профильные данные, отладочные данные, результаты анализов и т.д. + + Интерфейсы: + :: + + extern lccrt_eir_t lccrt_einfo_new_empty(); + extern lccrt_eir_t lccrt_einfo_new_i64( uint64_t value); + extern lccrt_eir_t lccrt_einfo_new_raw( lccrt_m_ptr m, int length, const uint8_t *data); + extern lccrt_eir_t lccrt_einfo_new_raw_by_string( lccrt_m_ptr m, const char *data); + extern lccrt_eir_t lccrt_einfo_new_struct( lccrt_eitd_ptr eitd); + extern lccrt_eir_t lccrt_einfo_new_array( lccrt_eitd_ptr eitd, int alloc_elems); + extern int lccrt_einfo_is_empty( lccrt_eir_t einfo); + extern int lccrt_einfo_is_valued( lccrt_eir_t einfo); + extern uint64_t lccrt_einfo_get_i64( lccrt_eir_t einfo); + extern uint8_t * lccrt_einfo_get_raw_data( lccrt_eir_t einfo); + extern lccrt_eir_t lccrt_einfo_get_elem( lccrt_eir_t einfo, int elem_ident); + extern lccrt_eir_t lccrt_einfo_get_field( lccrt_eir_t einfo, lccrt_eifi_t fid); + extern void lccrt_einfo_set_elem( lccrt_eir_t einfo, int elem_ident, lccrt_eir_t value); + extern void lccrt_einfo_set_field( lccrt_eir_t einfo, lccrt_eifi_t fid, lccrt_eir_t value); + extern void lccrt_einfo_push_elem( lccrt_eir_t einfo, lccrt_eir_t value); + extern int lccrt_einfo_get_num_args( lccrt_eir_t einfo); + extern int lccrt_einfo_get_raw_length( lccrt_eir_t einfo); + extern int lccrt_einfo_get_array_length( lccrt_eir_t einfo); + +* Типы **lccrt_einfo_tydescr_ptr** (**lccrt_eitd_ptr**) - описание типа дополнительных данных, **lccrt_einfo_field_id_t** (**lccrt_eifi_t**) - индекс поля в STRUCT-значении. + + Описание типа данных можно сравнить с оператором typedef в языке Си. Имеются два тривиальных + описания типов данных - это описания для INT64 и RAW. Данные тривиальные описания + используются как базовые элементы при создании описаний массивов и структур. При создании + описания массива нужно указать (произвольное) описание типа данных элементов массива. + + Интерфейсы: + + :: + + extern lccrt_einfo_tydescr_ptr lccrt_einfo_get_tydescr( lccrt_eir_t einfo); + extern int lccrt_einfo_is_tydescr_i64( lccrt_einfo_tydescr_ptr eitd); + extern int lccrt_einfo_is_tydescr_raw( lccrt_einfo_tydescr_ptr eitd); + extern lccrt_eitd_ptr lccrt_einfo_make_tydescr_i64( lccrt_m_ptr m); + extern lccrt_eitd_ptr lccrt_einfo_make_tydescr_raw( lccrt_m_ptr m); + extern lccrt_eitd_ptr lccrt_einfo_make_tydescr_array( lccrt_m_ptr m, lccrt_eitd_ptr elem_type); + + Если тривиальные описания и описание массивов не имеют тега-названия, то при создании + описаний структур необходимо указывать уникальный (в пределах модуля) тег-название. + Предлагается в качестве префикса в теге-названии использовать название логической + категории, в рамках которой планируется создавать дополнительные данные. Также при создании + описания структуры необходимо указать количество полей, строковые названия полей + внутри структуры и описания типов для каждого из полей. + + Интерфейсы: + + :: + + extern lccrt_eitd_ptr lccrt_einfo_make_tydescr_struct( lccrt_m_ptr m, const char *name, int num_flds, const char **flds_names, lccrt_eitd_ptr *flds_types); + + Для описания структуры по названию поля можно получить индекс поля (**lccrt_einfo_field_id_t**). + Индекс поля содержит в себе ссылку на описание структуры, а также данные для доступа к полю. + При создании ЭДД с типом **STRUCT** необходимо указать описание структуры. Соответственно чтение + и запись поля в таком элементе дополнительных данных возможно только через индекс поля. + Основное назначение индексов полей - обеспечить более быстрый доступ к полям по сравнению с + возможным доступом через строковые названия, а также обеспечить контроль типов. + + Интерфейсы: + :: + + extern lccrt_eifi_t lccrt_einfo_find_tydescr_field( lccrt_einfo_tydescr_ptr eitd, const char *fld_name); + extern lccrt_eir_t lccrt_einfo_get_field( lccrt_eir_t einfo, lccrt_eifi_t fid); + extern void lccrt_einfo_set_field( lccrt_eir_t einfo, lccrt_eifi_t fid, lccrt_eir_t value); + +Общие свойства дополнительных данных. +------------------------------------- + + В общем и целом для дополнительных данных использовался подход, приближенный к подходу + со строгой типизацией данных. Как следствие при практическом использовании текущей + реализации работа с дополнительными данными может требовать ряда рутинных действий. Создание + описание типа, поиск и сохранение идентификаторов категорий, формирование и сохранение + индексов полей в структурах. + + При реализации механизма дополнительных данных главным образом решалась задача трансляции + метаданных из llvm-подобного представления в расширенную информацию выходного представления. + Другими словами в основном решалась транзитная задача. Модификация дополнительных данных + возможна, но в текущей реализации отсутствует механизм удаления замещаемых данных. Все + дополнительные данные будут удалены только вместе с удалением модуля. Поэтому изменения + дополнительных данных возможно, но необходимо учитывать потребление памяти замещенными + данными. Наиболее простое решение в текущей реализации - это разработка прохода, который + пометит все достижимые дополнительные данные и автоматически удалит все недостижимые + дополнительные данные. + + Дополнительные данные сохраняются/читаются в текстовом формате вместе с основными данными + модуля промежуточного представления *lccrt-IR*. + +Примеры использования. +---------------------- + +* Рассмотрим пример использования дополнительных данных для транслятора *llvm-IR -> EIR*, использующем промежуточное представление *lccrt-IR* в качестве транзитного представления. + + В представлении *llvm-IR* для функции может быть указан набор атрибутов. + :: + + define dso_local i32 @main() local_unnamed_addr #0 { + ret i32 0 + } + + attributes #0 = { + mustprogress nofree norecurse nosync nounwind readnone willreturn + "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" + "stack-protector-buffer-size"="8" "target-cpu"="elbrus-v2" + } + +* Транслятор преобразует набор атрибутов в дополнительные данные, для чего предварительно создаются категория и описания типов. + :: + + // Создание категории "func_attrs" для привязки к функциям набора атрибутов. + ecat_func_attrs = lccrt_module_new_einfo_category( m, "func_attrs"); + + В том числе, создается описание структурного типа с тег-названием *"func_attrs.value"*. + Элементы дополнительных данных с данным структурным типом будут хранить значения атрибутов, + поэтому в качестве префикса в тег-названии использовалось значение *"func_attrs.\*"* + :: + + const char *fa_names[2] = {"src", "value"}; + lccrt_eitd_ptr fa_types[2] = {}; + + // Создание тривиальных описаний типов. + etyde_i64 = lccrt_einfo_make_tydescr_i64( m); + etyde_raw = lccrt_einfo_make_tydescr_raw( m); + + // Структурный тип "func_attrs.value" состоит из двух raw-полей: + // src - строка с информацией об источнике атрибута (в данном случае llvm-13) + // value - строка с названием и значением атрибута + fa_types[0] = etyde_raw; + fa_types[1] = etyde_raw; + etyde_func_attr = lccrt_einfo_make_tydescr_struct( m, "func_attrs.value", 2, fa_names, fa_types); + eifi_fattr_src = lccrt_einfo_find_tydescr_field( etyde_func_attr, "src"); + eifi_fattr_val = lccrt_einfo_find_tydescr_field( etyde_func_attr, "value"); + + Для привязки к функции набора атрибутов используются ЭДД с типом **ARRAY**, где элементами + массива являются структуры *"func_attrs.value"*. Такой ЭДД позволяет функции назначить сразу набор + (список) атрибутов. + :: + + etyde_func_attrs = lccrt_einfo_make_tydescr_array( m, etyde_func_attr); + + Также создается **RAW** данные со строкой "llvm-13" для повторного использования в качестве + значения поля *".src"* в структуре *"func_attrs.value"*. + :: + + eref_raw_llvm13 = lccrt_einfo_new_raw_by_string( m, "llvm-13"); + +* Затем для каждой функции из *llvm-IR* выполняется трансляция *llvm-атрибутов* в *lccrt-IR* формат дополнительных данных. + + Сначала создается массив (список) известной длины. + :: + + lccrt_eir_t attrs = lccrt_einfo_new_array( etyde_func_attrs, arr_len); + + Затем для каждого атрибута текущей функции создается структура "func_attrs.value". + :: + + attr = lccrt_einfo_new_struct( etyde_func_attr); + + Для обоих полей *".src"* и *".value"* формируются и устанавливаются значения. + :: + + v0 = eref_raw_llvm13; + v1 = lccrt_einfo_new_raw_by_string( m, it.getAsString( true).c_str()); + lccrt_einfo_set_field( attr, eifi_fattr_src, v0); + lccrt_einfo_set_field( attr, eifi_fattr_val, v1); + + После чего *k*-ый атрибут записывается в массив атрибутов. + :: + + lccrt_einfo_set_elem( attrs, k, attr); + + В завершении для функции устанавливается ссылка на список атрибутов в категории *"func_attrs"*. + Поскольку для функций наборы атрибутов могут целиком повторяться, то в трансляторе выполняется + хеширование для повторного переиспользования (*fai->second*), но для краткости операция + хеширование не приводится. + :: + + // Устанавливаем для функции набор атрибутов. + lccrt_function_set_einfo( f, ecat_func_attrs, fai->second); + //lccrt_function_set_einfo( f, ecat_func_attrs, attrs); + +* После передачи представления модуля выполняется создание целевого промежуточного представления и в данном случае - это представление *EIR*. + + При создании модуля в терминах представления EIR выполняется в том числе обход элементов + дополнительных данных. В начале выполняется поиск в модуле категории *"func_attrs"*. + :: + + lccrt_einfo_category_t attrs_ecat = lccrt_module_find_einfo_category( ci->lm, "func_attrs"); + + Затем для функции проверяется наличие данных в категории *"func_attrs"*. + :: + + if ( lccrt_einfo_category_is_valued( attrs_ecat) ) { + lccrt_einfo_reference_t eref = lccrt_function_get_einfo( f, attrs_ecat); + + if ( lccrt_einfo_is_valued( eref) ) { + // Чтение атрибутов функции. + ...; + } + } + + При первом проходе по списку атрибутов выполняется инициализация вспомогательных переменных. + :: + + if ( !fi->ci->etyde_fattr ) { + lccrt_einfo_tydescr_ptr etyde_fattrs = lccrt_einfo_get_tydescr( eref); + lccrt_einfo_tydescr_ptr etyde_fattr = lccrt_einfo_tydescr_get_elem( etyde_fattrs, 0); + + fi->ci->etyde_fattr = etyde_fattr; + fi->ci->eifi_fattr_src = lccrt_einfo_find_tydescr_field( etyde_fattr, "src"); + fi->ci->eifi_fattr_val = lccrt_einfo_find_tydescr_field( etyde_fattr, "value"); + } + + Сохранение индексов полей в локальные переменные. + :: + + fattr_src = fi->ci->eifi_fattr_src; + fattr_val = fi->ci->eifi_fattr_val; + + Проход по элементам списка атрибутов и обработка каждого атрибута. + :: + + int len = lccrt_einfo_get_num_args( eref); + + for ( i = 0; i < len; ++i ) { + lccrt_eir_t erefi = lccrt_einfo_get_elem( eref, i); + lccrt_eir_t src = lccrt_einfo_get_field( erefi, fattr_src); + lccrt_eir_t val = lccrt_einfo_get_field( erefi, fattr_val); + + lccrt_ProcessClFuncAttr( fi->proc, (char *)lccrt_einfo_get_raw_data( src), (char *)lccrt_einfo_get_raw_data( val)); + } + + Обработка строкового значения конкретного атрибута с учетом его источника выполняется + в данном случае в функции *lccrt_ProcessClFuncAttr*. + +* В текстовом формате дополнительные данные будет сохранены следующим образом. + + Привязка дополнительных данных к функции выполняется с помощью директивы *.einfo("%c1:%e25")*. + В данном случае *%c1* - это сокращение для категории, а *%e25* - сокращение для дексриптора ЭДД. + :: + + func main : () -> u32 .bind(glob) .nothrow .einfo("%c1:%e25") + { + __lccrt_l0: + ret __lccrt_c0; + __lccrt_l1: + } + + Регистрация в модуле категории *"func_attrs"*. + :: + + eicat %c1 = func_attrs; + + Определение описаний типов данных *"func_attrs.value"* и массива таких данных. + :: + + eityde %s4 = struct "func_attrs.value" {src:raw,value:raw}; + eityde %s5 = array %s4; + + Опредение дескрипторов ЭДД и их значений. Перед значением дескриптора приводится его тип. + :: + + eidef %e0 = raw {"target-cpu=elbrus-v2"}; + eidef %e1 = raw {"llvm-13"}; + eidef %e2 = %s4 {%e1,%e0}; + ... + eidef %e25 = %s5 {%e24,%e22,%e20,%e18,%e16,%e14,%e12,%e10,%e8,%e6,%e4,%e2}; +