2013-04-15 2 views
7

Проверьте код ниже:может GCC распечатать промежуточные результаты?

#include <avr/io.h> 

const uint16_t baudrate = 9600; 

void setupUART(void) { 
     uint16_t ubrr = ((F_CPU/(16 * (float) baudrate)) - 1 + .5); 
     UBRRH = ubrr >> 8; 
     UBRRL = ubrr & 0xff; 
} 

int main(void) { 
     setupUART(); 
} 

Эта команда используется для компиляции кода:

avr-gcc -g -DF_CPU=4000000  -Wall -Os -Werror -Wextra -mmcu=attiny2313 -Wa,-ahlmns=project.lst -c -o project.o project.cpp 

ubrr вычисляется компилятором как 25, до сих пор так хорошо. Однако, чтобы проверить, что вычисляет компилятор, я заглянул в список разборки.

000000ae <setupUART()>: 
    ae: 12 b8   out  UBRRH, r1  ; 0x02 
    b0: 89 e1   ldi  r24, 0x19  ; 25 
    b2: 89 b9   out  UBRRL, r24  ; 0x09 
    b4: 08 95   ret 

Можно ли сделать avr-gcc распечатать промежуточный результат во время компиляции (или вытащить данные из файла .o), поэтому, когда я скомпилировать код печатает строку, как (uint16_t) ubbr = 25 или подобное? Таким образом, я могу быстро проверить настройки и настройки.

+0

Вы пробовали '-S' вариант? – devnull

+0

@devnull Не выходит ли из компилятора? Я хочу, чтобы компилятор завершил работу, просто распечатайте промежуточный расчет, который он сделал. – jippie

+0

Если вы просто не передаете '-O', вы, вероятно, получите именно то, что хотите увидеть при разборке. Вы, вероятно, не хотите грузить таким образом. ;-) –

ответ

3

GCC имеет параметры командной строки, чтобы запросить, чтобы он выгружал свое промежуточное представление после любого этапа компиляции. «Деревовые» свалки находятся в псевдо- C и содержать нужную вам информацию. Для того, что вы пытаетесь сделать, дампы -fdump-tree-original и -fdump-tree-optimized происходят в полезных точках в optimi . У меня нет AVR компилятор под рукой, поэтому я изменил свой тест, чтобы быть самодостаточным и скомпилированы с компилятором у меня есть:

typedef unsigned short uint16_t; 
const int F_CPU = 4000000; 
const uint16_t baudrate = 9600; 
extern uint16_t UBRRH, UBRRL; 

void 
setupUART(void) 
{ 
    uint16_t ubrr = ((F_CPU/(16 * (float) baudrate)) - 1 + .5); 
    UBRRH = ubrr >> 8; 
    UBRRL = ubrr & 0xff; 
} 

и затем

$ gcc -O2 -S -fdump-tree-original -fdump-tree-optimized test.c 
$ cat test.c.003t.original 
;; Function setupUART (null) 
;; enabled by -tree-original 


{ 
    uint16_t ubrr = 25; 

    uint16_t ubrr = 25; 
    UBRRH = (uint16_t) ((short unsigned int) ubrr >> 8); 
    UBRRL = ubrr & 255; 
} 

$ cat test.c.149t.optimized 
;; Function setupUART (setupUART, funcdef_no=0, decl_uid=1728, cgraph_uid=0) 

setupUART() 
{ 
<bb 2>: 
    UBRRH = 0; 
    UBRRL = 25; 
    return; 
} 

Вы можете увидеть что складывание с постоянным выражением выполняется так рано, что это уже произошло в «исходном» дампе (который является самым ранним приемлемым дампом, который у вас есть), и эта оптимизация еще больше сместила операции сдвига и маски в операторы, записывающие UBRRH и UBRRL ,

Номера в именах файлов (003t и 149t), вероятно, будут отличаться для вас. Если вы хотите увидеть все «деревенские» свалки, используйте -fdump-tree-all. Существуют также «RTL» дампы, которые не похожи на C и, вероятно, вам не пригодились. Если вам интересно, то -fdump-rtl-all включится. Всего имеется около 100 деревьев и 60 отвалов RTL, поэтому рекомендуется сделать это в каталоге с нуля.

(Psssst: Каждый раз, когда вы кладете пространства внутри ваших скобок, Бог убивает котенка.)

+0

Ницца! Я могу сделать 'grep -E '^ [\ t] * uint16_t ubrr' project.cpp.003t.original', который выглядит как удобное для меня время от времени – jippie

+0

http://i.stack.imgur.com/ofkaC.png – jippie

2

Может быть решение для печати промежуточных результатов, но потребуется некоторое время для реализации. Поэтому это стоит только для довольно большой базы исходного кода.

Вы можете настроить свой компилятор GCC; либо через плагин (болезненно закодированный на C или C++), либо через расширение MELT. MELT - это язык высокого уровня, похожий на Lisp, для расширения GCC. (Он реализован как плагин [meta-] для GCC и переведен на код C++, подходящий для GCC).

Однако такой подход требует, чтобы понять GCC внутренностей, а затем добавить свою собственную «оптимизацию» передать сделать aspect oriented programming (например, с использованием Растопить) для печати соответствующих промежуточных результатов.

Вы также можете посмотреть не только сгенерированную сборку (и использовать -fverbose-asm -S как опции для GCC), но также, возможно, в сгенерированных представлениях Gimple (возможно, с -fdump-tree-gimple). Для некоторых интерактивных инструментов рассмотрим графический файл MELT probe.

Возможно, добавление собственного встроенного устройства (с расширением MELT), например __builtin_display_compile_time_constant, может иметь значение.

1

Я сомневаюсь, что есть простой способ определить, что делает компилятор. В gcc могут быть некоторые инструменты, чтобы сбрасывать промежуточную форму языка, но это определенно нелегко будет читать, и если вы НЕ НАСТОЯЩИМ подозреваете, что компилятор делает что-то неправильно (и у него есть ОЧЕНЬ маленький пример, чтобы показать его) , маловероятно, что вы можете использовать его для чего-либо значимого - просто потому, что слишком много работы, чтобы следить за тем, что происходит.

Лучший подход заключается в добавлении временных переменных (и, возможно, печатает) в код, если вы беспокоитесь о том, что является правильным:

uint16_t ubrr = ((F_CPU/(16 * (float) baudrate)) - 1 + .5); 
    uint8_t ubrr_high = ubrr >> 8 
    uint8_t ubrr_low = ubrr & 0xff; 
    UBRRH = ubrr_high; 
    UBRRL = ubrr_low; 

Теперь, если у вас есть неоптимизированную сборки и шаг за шагом через него в GDB вы должны уметь видеть, что он делает. В противном случае добавление распечаток какого-либо кода в код, чтобы показать, какие значения ...

Если вы не можете распечатать его в целевой системе, потому что вы находитесь в процессе настройки uart, который вы будете использовать для печати, а затем реплицировать код на вашей локальной хост-системе и отлаживать его там. Если компилятор не очень глючит, вы должны получить одинаковые значения из одной и той же компиляции.

1

Вот хак: просто автоматизируйте то, что вы делаете вручную.

  • В вашем Makefile, убедитесь, что АРН-GCC производит разборку (-ahlms=output.lst). В качестве альтернативы, используйте свой метод дизассемблирования как шаг после компиляции в вашем файле makefile.
  • Как шаг после компиляции, обработайте файл с помощью любимого языка сценариев для поиска out UBRRH и out UBRRL строк. Они будут загружаться из регистров, поэтому ваш скрипт может вывести сразу же предшествующие задания в регистры, которые будут загружены в UBRRH и UBRRL. Сценарий затем может собрать значение UBRR из значения, загруженного в регистры общего назначения, которые используются для установки UBRRH и UBRRL.

Это звучит проще, чем Basile Starynkevich 's очень полезное предложение MELT расширение. Теперь, как должное, что это решение кажется хрупким, на первый взгляд, так что давайте рассмотрим этот вопрос:

  • Мы знаем, что (по крайней мере, на процессоре) линии out UBRR_, r__ появится в разборке листинг: есть просто нет другого способа установить регистры/записать данные в порт. Одна вещь, которая может измениться, - это расстояние между этими строками, но это может быть легко обработано вашим скриптом
  • Мы также знаем, что инструкции out могут выполняться только из регистров общего назначения, поэтому мы знаем, как второй аргумент строки инструкции out, так что это не должно быть проблемой.
  • Наконец, мы также знаем, что этот регистр будет установлен до команды out. Здесь мы должны допускать некоторую изменчивость: вместо LDI (загрузка немедленно), avr-gcc может создать другой набор инструкций для установки значения регистра. Я думаю, что в качестве первого прохода скрипт должен иметь возможность анализировать немедленную загрузку и в противном случае сбрасывать любую последнюю инструкцию, которую он находит, с использованием регистра, который будет записан в порты UBRR_.

Сценарий, возможно, придется изменить, если вы меняете платформ (некоторые процессоры имеют UBRRH1/2 регистры instea из UBRRH, однако в этом случае вы бод код будет меняться. Если скрипт жалуется, что не может разобрать то вы, по крайней мере, знаете, что ваш чек не был выполнен.

+0

UBRRH покажет r1, который при запуске обычно устанавливается равным 0 (с использованием 'eor r1, r1', но это действительно трудно быть уверенным в этом. – jippie

+0

Ну, в худшем случае ваш скрипт может напечатать' eor r1, r1' используя «распечатать последнюю вещь, связанную с регистром, который используется для установки эвристического порта». Или вы могли бы написать мини-AVR-симулятор в своем скрипте, обучая его тому, что XOR-значение с самим собой означает «установить его на ноль»:) – angelatlarge

 Смежные вопросы

  • Нет связанных вопросов^_^