gcc оптимизация, постоянный статический объект и ограничение - PullRequest
4 голосов
/ 03 февраля 2010

Я работаю над встроенным проектом и пытаюсь добавить больше структуры в некоторый код, который использует макросы для оптимизации доступа к регистрам для USART. Я хотел бы организовать препроцессор # define'd адреса регистров в структуры const. Если я определяю структуры как составные литералы в макросе и передаю их встроенным функциям, gcc достаточно умен, чтобы обойти указатель в сгенерированной сборке и жестко закодировать значения элементов структуры непосредственно в коде. E.g.:

С1:

struct uart {
   volatile uint8_t * ucsra, * ucsrb, *ucsrc, * udr;
   volitile uint16_t * ubrr;
};

#define M_UARTX(X)                  \
    ( (struct uart) {               \
        .ucsra = &UCSR##X##A,       \
        .ucsrb = &UCSR##X##B,       \
        .ucsrc = &UCSR##X##C,       \
        .ubrr  = &UBRR##X,          \
        .udr   = &UDR##X,           \
    } )


void inlined_func(const struct uart * p, other_args...) {
    ...
    (*p->ucsra) = 0;
    (*p->ucsrb) = 0;
    (*p->ucsrc) = 0;
}
...
int main(){
     ...
     inlined_func(&M_UART(0), other_parms...);
     ...
}

Здесь UCSR0A, UCSR0B, & c определяются как регистры uart как l-значения, например

#define UCSR0A (*(uint8_t*)0xFFFF)

gcc смог полностью исключить литерал структуры, и все назначения, подобные показанным в inlined_func (), записываются непосредственно в адрес регистра, без необходимости считывать адрес регистра в регистр компьютера и без косвенной адресации. :

A1:

movb $0, UCSR0A
movb $0, UCSR0B
movb $0, UCSR0C

Это записывает значения непосредственно в регистры USART, без необходимости загружать адреса в машинный регистр, и поэтому вообще не нужно генерировать литерал struct в объектном файле. Литерал структуры становится структурой времени компиляции без затрат в сгенерированном коде для абстракции.

Я хотел избавиться от использования макроса и попытался использовать статическую константу struct, определенную в заголовке:

С2:

#define M_UART0 M_UARTX(0)
#define M_UART1 M_UARTX(1)

static const struct uart * const uart[2] = { &M_UART0, &M_UART1 };
....
int main(){
     ...
     inlined_func(uart[0], other_parms...);
     ...
}

Однако gcc не может полностью удалить структуру здесь:

A2

movl __compound_literal.0, %eax
movb $0, (%eax)
movl __compound_literal.0+4, %eax
movb $0, (%eax)
movl __compound_literal.0+8, %eax
movb $0, (%eax)

Это загружает адреса регистров в машинный регистр и использует косвенную адресацию для записи в регистр. Кто-нибудь знает в любом случае, я могу убедить gcc сгенерировать ассемблерный код A1 для кода C2 C? Я пробовал различные варианты использования модификатора __restrict, но безрезультатно.

Ответы [ 2 ]

2 голосов
/ 03 февраля 2010

После многолетнего опыта работы с UART и USART я пришел к следующим выводам:

Не используйте struct для отображения 1: 1 с регистрами UART.

Компиляторы могут добавлять отступы между struct членами без вашего ведома, таким образом портя соответствие 1: 1.

Запись в регистры UART лучше всего выполнять напрямую или с помощью функции.

Не забудьте использоватьМодификатор volatile при определении указателей на регистры.

Очень низкий прирост производительности при использовании языка ассемблера

Язык ассемблера следует использовать только в том случае, если доступ к UART осуществляется через порты процессора, а не в память.Язык C не поддерживает порты.Доступ к регистрам UART через указатели очень эффективен (создайте список на ассемблере и проверьте).Иногда для сборки и проверки кода может потребоваться больше времени.

Выделение функциональности UART в отдельной библиотеке

Это хороший кандидат.Кроме того, как только код будет протестирован, пусть будет так.Библиотеки не нужно (пере) компилировать постоянно.

1 голос
/ 03 февраля 2010

Использование структур "через домены компиляции" - главный грех в моей книге. В основном, использование структуры для указания чего-либо, чего-либо, файловых данных, памяти и т. Д. И причина в том, что он потерпит неудачу, он не надежен, независимо от компилятора. Для этого существует множество специфических для компилятора флагов и прагм. Лучшее решение - просто не делать этого. Вы хотите указать по адресу плюс 8, указать по адресу плюс 8, использовать указатель или массив. В этом конкретном случае у меня было слишком много компиляторов, которые не могли этого сделать, и я пишу функции ассемблера PUT32 / GET32 PUT16 / GET16, чтобы гарантировать, что компилятор не связывается с моими обращениями к реестру, например, структурами, вы однажды будете сожжены и Имейте чертовское время, выясняющее, почему ваш 32-битный регистр записал в него только 8 бит. Затраты на переход к функции стоят спокойствия, а также надежности и переносимости кода. Кроме того, это делает ваш код чрезвычайно переносимым, вы можете помещать оболочки для функций put и get для кросс-сетей, запускать аппаратное обеспечение в симуляторе hdl и получать доступ к симуляции для чтения / записи регистров и т. Д. С помощью одного куска кода, который не изменяется от симуляции до встроенного в драйвер устройства OS функции уровня приложения.

...