Элегантный способ определить автоматическую переменную с определенным выравниванием - PullRequest
0 голосов
/ 29 ноября 2018

Я работаю с компилятором ARM и имею HW-периферию (имеющую прямой доступ к памяти), которая требует специального выравнивания для буферов памяти, передаваемых ему (32-байтовое выравнивание).Это не проблема, когда буферы являются глобальными / статическими и могут быть определены с помощью атрибута aligned, поддерживаемого компилятором.Проблема возникает всякий раз, когда возникает необходимость передать буфер, определенный в некоторой функции, локально, то есть с классом автоматического хранения.Я попытался сделать что-то похожее на следующее:

typedef struct  __attribute__((aligned(32)))
{
    char bytes[32];
} aligned_t;

_Static_assert(sizeof(aligned_t)==32, "Bad size");

void foo(void)
{
    aligned_t alignedArray[NEEDED_SIZE/sizeof(aligned_t)];
    //.... use alignedArray
}

, и это было успешно скомпилировано и работало на компиляторе x86 .Но не в armcc , который жалуется:

Предупреждение: # 1041-D: выравнивание для автообъекта не должно превышать 8

Так что этот подход не работает.Есть еще один, который я считаю уродливым:

void foo(void)
{
    char unalignedBuffer[NEEDED_SIZE + 32 - 1];
    char pAlignedBuffer = ALIGN_UP_32(unalignedBuffer);
    //.... use pAlignedBuffer
}

, в то время как ALIGN_UP_32 - это макрос, который возвращает первый выровненный адрес в unalignedBuffer (детали реализации здесь не важны, я думаю).

Как я уже сказал, мне не нравится этот подход, и мне интересно, есть ли более элегантный способ добиться того же?

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Ваши два варианта выглядят как самые простые.Однако (и только догадываясь, я не очень задумывался о своем собственном ответе), другим вариантом может быть создание другого стека.Когда функция, которая содержит ваш буфер, выполняется, контекст переключается (ну, просто SP - в режиме супервизора), и теперь SP указывает на второй стек.Этот стек размещается в 32-битном выровненном разделе, и он будет содержать только 32-битные выровненные объекты, поэтому, когда создается локальная 32-битная выровненная переменная, он будет размещен в 32-битном выровненном блоке памяти, который будет освобожден после выхода из области видимости.,Как только функция выполнена, SP переключается обратно в основной стек.Выполнение функции должно рассматриваться как критическая область, чтобы избежать нажатия / выталкивания в неправильном стеке.Я не думаю, что это приведет к переполнению стека, но, как я уже сказал, я отвлекся, на всякий случай, если это поможет ...

0 голосов
/ 29 ноября 2018

Я работаю с компилятором ARM

Вы также пробовали недавний GCC (возможно, настроенный как кросс-компилятор)Например, GCC 8 в ноябре 2018 года?

ARI ABI не гарантирует, что указатель стека (вероятно) будет выровнен до 32 байтов.

Таким образом, любая автоматическая переменная не выровнена настолькоВы хотите.

Вы можете избежать их (и систематически использовать соответственно выровненную зону памяти кучи).Или вы могли бы выделить больше, чем нужно, и сделать арифметику с указателями.

Я чувствую, что ваш char* pAlignedBuffer = ALIGN_UP_32(unalignedBuffer); - это хороший подход, и я считаю, что оптимизирующий компилятор генерирует довольно эффективный код.

Мне не нравится этот подход, и мне интересно, есть ли более элегантный способ добиться того же?

Я считаю, что ваш подход хорош, и любой другой способ будет эквивалентен.

PS.Другой подход может заключаться в исправлении вашего компилятора GCC (возможно, с помощью плагина) для изменения выравнивания по умолчанию указателя стека (следовательно, эффективного изменения ваших ABI и соглашений о вызовах ).Это займет у вас недели (или месяцы) усилий.

...