ecomp.r29.144265

 :
   liblccopt: ecomp.r29.143691 - ecomp.r29.144265
This commit is contained in:
stepanov 2024-04-24 17:12:30 +03:00
parent b6d9981214
commit 84378aa8a0

356
docs/einfo.rst Normal file
View File

@ -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};