Как эффективно инициализировать изменчивую структуруC++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Как эффективно инициализировать изменчивую структуру

Сообщение Anonymous »

Я хотел бы использовать битовые поля для доступа к памяти низкого уровня. Я знаю о непереносимости битовых полей, но, похоже, они последовательно реализованы на моей платформе (cortex-m4) как в clang, так и в gcc. Однако я обнаружил, что присвоение всего битового поля одновременно генерирует значительно больше инструкций ассемблера для clang, чем для gcc. Упрощенный пример моей цели показан ниже:

Код: Выделить всё

struct B {
struct {
volatile int b:3;
volatile int c:3;
volatile int d:26;
} a;
} bmem;

constexpr B * const b = &bmem;

void fun() {
b->a = { .b = 1, .c = 2 };
}
Ссылка на исследование компилятора
При переключении между Armv7-a Clang 21.1.0 и Arm GCC 15.2.0 можно увидеть, что компилятор GCC оптимизирует инициализацию структуры до 3 инструкций, в то время как Clang принимает 16. Похоже, что clang обрабатывает значения rhs как изменчивые, а gcc - нет. Я пробовал различные варианты, чтобы сократить количество инструкций, генерируемых clang, но не смог найти надежного способа заставить это работать. Самое близкое, что я мог бы сделать без большого количества дополнительного кода, — это пометить указатель структуры b как указатель на изменчивую, а не на члены структуры B. А затем сделать это объединением и использовать незаконный (из-за неопределенного поведения) каламбур типа объединения. Однако мне не нравится это решение. Есть ли простой способ указать компилятору, что я хотел бы установить это значение памяти, не рассматривая значение инициализации как изменчивое?
Вот версия каламбура типа объединения:

Код: Выделить всё

struct B {
union {
int u;
struct {
int b:3;
int c:3;
int d:26;
} a;
};
} bmem;

constexpr volatile B * const b = &bmem;

void fun() {
b->u = B { .a = { .b = 1, .c = 2 } }.u;
}
Дополнительная информация
Спасибо за комментарии ниже. Для получения дополнительной информации я ищу доступ к аппаратным регистрам, отображаемым в памяти. Я должен был прояснить, что я не пытаюсь осуществлять взаимодействие потоков, хотя я могу себе представить, что решения для взаимодействия потоков могут дать представление о том, как работать с отображаемыми в памяти регистрами. Обычно статус-кво для этой платформы заключается в использовании файлов заголовков, предоставленных производителем. Эти файлы заголовков могут принимать две разные формы, но обе основаны на изменчивых. Первая форма — это изменчивые регистры uint32_t плюс серия битовых масок #define. Вторая форма представляет собой объединение битовых полей, подобное примеру кода № 2 выше, за исключением того, что члены объединения и структуры определены изменчивыми. Я широко использую первую форму и экспериментирую со второй формой, чтобы уменьшить количество битовых логических ошибок и улучшить проверку типов и диапазонов.
каламбур типа объединения
В комментариях я изучил каламбур типа объединения и обнаружил, что и clang, и gcc в настоящее время признают, что он работает с некоторыми оговорками. Согласно их предостережениям, я немного обеспокоен тем, что могу допустить ошибку, неправильно используя указатели. Я также предпочитаю безопасность типов, которую дает пример кода №1.
Встроенные функции атома
Теперь я попробовал их и обнаружил, что void __atomic_store (type *ptr, type *val, int memorder) с memorder __ATOMIC_RELAXED дает мне работающую систему. Однако я обнаружил, что он также не имеет безопасности типов, что позволяет мне по какой-то причине устанавливать любое значение val в любой тип ptr. Ему также не хватает возможности использовать установку битового поля напрямую, например b->a.c = 3, без использования атомных_загрузок и атомных_магазинов. В этом случае энергозависимая версия ведет себя так, как мне хотелось бы. Я также обнаружил, что если я скомпилирую с -flto, мне придется пометить экземпляр b с помощью __attribute__((used)).
Профилирование
На этой платформе я очень хорошо осведомлен о стоимости установки регистров из-за отслеживания регистров, установленных во время прерываний. На практике эти битовые поля могут иметь более трех значений, а количество инструкций зависит от количества членов битового поля в clang. На этой платформе конвейер относительно прост, поэтому количество циклов в этом случае обычно довольно пропорционально количеству инструкций.

Подробнее здесь: https://stackoverflow.com/questions/798 ... ile-struct
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»