mirror of
https://gitflic.ru/project/e2khome/lccrt.git
synced 2024-12-02 23:00:08 +01:00
84378aa8a0
: liblccopt: ecomp.r29.143691 - ecomp.r29.144265
357 lines
24 KiB
ReStructuredText
357 lines
24 KiB
ReStructuredText
======================================================================
|
||
Дополнительные данные (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};
|
||
|