Думаете - он компилит оптимальнее?
Ассемблер для STM32. Сложно ли, стоит ли пытаться?
- Сообщения: 1743
- Зарегистрирован: Вт авг 15, 2017 10:51:13
[uquote="NStorm",url="/forum/viewtopic.php?p=3741454#p3741454"]GCC и нет проблем.[/uquote]
Думаете - он компилит оптимальнее?
Думаете - он компилит оптимальнее?
- Реклама
Несколько лет назад GCC давал отвратительный код; не думаю, чтобы что-то сильно изменилось. Кейловский (родной, не Кланг) куда лучше выдавал, но там другая проблема: АРМ забила на свой компилятор и переключилась на Кланг, который нередко выдаёт менее качественный код. Вообще, не помнить содержимое регистров и повторно производить загрузки или же заниматься бесполезными пересылками из регистра в регистр -- это нынче норма жизни для компиляторов.
- Сообщения: 1978
- Зарегистрирован: Ср июл 17, 2013 13:55:57
А можно какой-нибудь пример подобного кода, который будет неэффективно скомпилен? Не ради докопаться или поспорить, но любопытно и правда посмотреть, попробовать разобраться. Может там ключиками решается или еще что. Да и просто знать подобные подводные камни.
- Сообщения: 1743
- Зарегистрирован: Вт авг 15, 2017 10:51:13
[uquote="NStorm",url="/forum/viewtopic.php?p=3741541#p3741541"]А можно какой-нибудь пример подобного кода, который будет неэффективно скомпилен? Не ради докопаться или поспорить, но любопытно и правда посмотреть, попробовать разобраться. Может там ключиками решается или еще что. Да и просто знать подобные подводные камни.[/uquote]Например вот вчера наткнулся:
Pval() - это у меня макрос чтения состояния одного бита порта GPIO (этот макрос для PIN_PRND_B должен развернуться в
(*(u32 volatile *)IO_ADDR >> 0 & 1).
Как видно - строчку "c -= Pval(PIN_PRND_B) * 2" я написал специально с таким расчётом, чтобы компилятор после LDR сделал всего две команды: выделение бита из считанного слова и вычитание его из c (думал помочь ему
). Но он зачем-то влепил 3 команды. 
Код: Выделить всё
uint c = 3 - Pval(PIN_PRND_F);
0xF8D5 0x2524 LDR R2,[R5, #+1316]
0xF3C2 0x2280 UBFX R2,R2,#+10,#+1
0xF1C2 0x0203 RSB R2,R2,#+3
c -= Pval(PIN_PRND_B) * 2;
0xF8D5 0x3624 LDR R3,[R5, #+1572]
0x005B LSLS R3,R3,#+1
0xF003 0x0302 AND R3,R3,#0x2
0x1AD3 SUBS R3,R2,R3(*(u32 volatile *)IO_ADDR >> 0 & 1).
Как видно - строчку "c -= Pval(PIN_PRND_B) * 2" я написал специально с таким расчётом, чтобы компилятор после LDR сделал всего две команды: выделение бита из считанного слова и вычитание его из c (думал помочь ему
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
jcxz, а можно твой вариант, как ты хотел чтобы было?
- Реклама
- Сообщения: 1978
- Зарегистрирован: Ср июл 17, 2013 13:55:57
Дык volatile же. Поэтому всё строго. Сначала свдиг, потом И.
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
После чтения из IO в регистр никакого volatile больше нет.
- Сообщения: 1743
- Зарегистрирован: Вт авг 15, 2017 10:51:13
[uquote="VladislavS",url="/forum/viewtopic.php?p=3741579#p3741579"]jcxz, а можно твой вариант, как ты хотел чтобы было?[/uquote]
Ну как минимум для строчки "c -= Pval(PIN_PRND_B) * 2" я ожидал чего-то типа:или:
т.е. - 2 команды, по 4 байта каждая.
А в идеале я конечно хотел бы увидеть:т.е. - 2 команды, 2+4 байта. Но на такое я уже не надеялся практически. Так только человек сможет оптимизировать. 
Добавлено after 11 minutes 30 seconds:
[uquote="NStorm",url="/forum/viewtopic.php?p=3741583#p3741583"]Дык volatile же. Поэтому всё строго. Сначала свдиг, потом И.[/uquote]volatile действует только на операции доступа к памяти - не должен меняться их порядок. А к содержимому регистров отношения не имеет. Что и подтверждается компиляцией того же самого кода в режиме максимальной оптимизации, при которой компилятор создаёт те же самые команды, только вначале ставит обе LDR одна за другой, а потом после - все остальные арифметические команды:
Это High-Balanced оптимизация (то что выше было на Medium). Как видно - ума она компилятору не добавила, он просто переставил команды и всё.
Ну как минимум для строчки "c -= Pval(PIN_PRND_B) * 2" я ожидал чего-то типа:
Код: Выделить всё
LDR R3, [R5, #+1572]
AND R3, R3, #0x1
SUBS R3, R2, R3, LSL #1Код: Выделить всё
LDR R3, [R5, #+1572]
UBFX R3, R3, #0, #1
SUBS R3, R2, R3, LSL #1А в идеале я конечно хотел бы увидеть:
Код: Выделить всё
LDR R3,[R5, #+1572]
LSLS R3, R3, #31
ADDS R3, R2, R3, ASR #30Добавлено after 11 minutes 30 seconds:
[uquote="NStorm",url="/forum/viewtopic.php?p=3741583#p3741583"]Дык volatile же. Поэтому всё строго. Сначала свдиг, потом И.[/uquote]volatile действует только на операции доступа к памяти - не должен меняться их порядок. А к содержимому регистров отношения не имеет. Что и подтверждается компиляцией того же самого кода в режиме максимальной оптимизации, при которой компилятор создаёт те же самые команды, только вначале ставит обе LDR одна за другой, а потом после - все остальные арифметические команды:
Код: Выделить всё
uint c = 3 - Pval(PIN_PRND_F);
0xF8D5 0x2524 LDR R2,[R5, #+1316]
c -= Pval(PIN_PRND_B) * 2;
0xF8D5 0x3624 LDR R3,[R5, #+1572]
0xF3C2 0x2280 UBFX R2,R2,#+10,#+1
0x005B LSLS R3,R3,#+1
0xF1C2 0x0203 RSB R2,R2,#+3
0xF003 0x0302 AND R3,R3,#0x2
0x1AD6 SUBS R6,R2,R3- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
[uquote="jcxz",url="/forum/viewtopic.php?p=3741669#p3741669"]т.е. - 2 команды, по 4 байта каждая.[/uquote]Размер кода тот же. Биться об заклад не буду, но что-то мне подсказывает, что и выполняться он будет за то же время.
- Сообщения: 1743
- Зарегистрирован: Вт авг 15, 2017 10:51:13
[uquote="VladislavS",url="/forum/viewtopic.php?p=3741691#p3741691"]Размер кода тот же. Биться об заклад не буду, но что-то мне подсказывает, что и выполняться он будет за то же время.[/uquote]В смысле "тот же"? Кто с кем?
[uquote="jcxz",url="/forum/viewtopic.php?p=3741669#p3741669"][uquote="VladislavS",url="/forum/viewtopic.php?p=3741579#p3741579"]jcxz, а можно твой вариант, как ты хотел чтобы было?[/uquote]
Ну как минимум для строчки "c -= Pval(PIN_PRND_B) * 2" я ожидал чего-то типа:
или:
т.е. - 2 команды, по 4 байта каждая.
А в идеале я конечно хотел бы увидеть:т.е. - 2 команды, 2+4 байта. Но на такое я уже не надеялся практически. Так только человек сможет оптимизировать.[/uquote]
Что касается последнего варианта, то он не всегда оптимален: зависит от конкретного варианта конкретного ядра. Дело в том, что многоразрядный сдвиг за 1 такт требует соответствующего сдвигателя, а им могут пожертвовать для уменьшения размеров проца. И тогда LSLS будет сдвигать по одному биту за такт
Зато если есть гарантия, что сдвиг будет за 1 такт, он действительно лучший (те же 2 такта, не считая собственно выборки из памяти, но 6 байтов, а не 8 ).
Ну как минимум для строчки "c -= Pval(PIN_PRND_B) * 2" я ожидал чего-то типа:
Код: Выделить всё
LDR R3, [R5, #+1572]
AND R3, R3, #0x1
SUBS R3, R2, R3, LSL #1Код: Выделить всё
LDR R3, [R5, #+1572]
UBFX R3, R3, #0, #1
SUBS R3, R2, R3, LSL #1А в идеале я конечно хотел бы увидеть:
Код: Выделить всё
LDR R3,[R5, #+1572]
LSLS R3, R3, #31
ADDS R3, R2, R3, ASR #30Что касается последнего варианта, то он не всегда оптимален: зависит от конкретного варианта конкретного ядра. Дело в том, что многоразрядный сдвиг за 1 такт требует соответствующего сдвигателя, а им могут пожертвовать для уменьшения размеров проца. И тогда LSLS будет сдвигать по одному биту за такт
- Сообщения: 3385
- Зарегистрирован: Пн окт 11, 2010 19:00:08
Результат GCC.
Код: Выделить всё
uint32_t c = 3 - Pval(PIN_PRND_F);
ldr r2, [pc, #108] ; (0x20000338 <main+112>)
ldr r3, [r2, #0]
ands r3, r1
rsb r3, r3, #3
c -= Pval(PIN_PRND_B) * 2;
ldr r0, [pc, #104] ; (0x2000033c <main+116>)
ldr r4, [r0, #0]
ands r4, r1
sub.w r3, r3, r4, lsl #1- Сообщения: 270
- Зарегистрирован: Вс окт 20, 2019 13:03:56
А вот хотел спросить, вообще есть какая нибудь книга, или руководство по программированию этих стм32 на Си, вроде Фрунзе и Магды для 8051, или Ревича и Белова для AVR?
Я так понимаю, про комповый Си (как и про комповый ассемблер) читать бесполезно, там все по-другому.
Я так понимаю, про комповый Си (как и про комповый ассемблер) читать бесполезно, там все по-другому.
Последний раз редактировалось Shuspano Сб ноя 23, 2019 15:20:37, всего редактировалось 1 раз.
- Сообщения: 2516
- Зарегистрирован: Пт июл 12, 2019 22:52:01
- Сообщения: 270
- Зарегистрирован: Вс окт 20, 2019 13:03:56
[uquote="Eddy_Em",url="/forum/viewtopic.php?p=3742030#p3742030"]А чем же "комповый" С отличается от "железячного"?
Абсолютно одно и то же![/uquote]
Разьве? Разьве там есть такое?
Абсолютно одно и то же![/uquote]
Разьве? Разьве там есть такое?
Код: Выделить всё
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);- Сообщения: 3385
- Зарегистрирован: Пн окт 11, 2010 19:00:08
Разве в комповом Си нет функций и структур?Shuspano писал(а):Разве там есть такое?
То что вы спрашиваете относится не к языку Си, а в библиотеке SPL.
- Сообщения: 270
- Зарегистрирован: Вс окт 20, 2019 13:03:56
[uquote="Мурик",url="/forum/viewtopic.php?p=3742040#p3742040"]
То что вы спрашиваете относится не к языку Си, а в библиотеке SPL.[/uquote]
Да мне пока без разницы. Щас пока беспокоит вопрос: где и как узнать, что собственно писать, где описаны эти инструкции.
Разве в комповом Си нет функций и структур?Shuspano писал(а):Разве там есть такое?
То что вы спрашиваете относится не к языку Си, а в библиотеке SPL.[/uquote]
Да мне пока без разницы. Щас пока беспокоит вопрос: где и как узнать, что собственно писать, где описаны эти инструкции.
- Сообщения: 1743
- Зарегистрирован: Вт авг 15, 2017 10:51:13
[uquote="SII",url="/forum/viewtopic.php?p=3741738#p3741738"]Что касается последнего варианта, то он не всегда оптимален: зависит от конкретного варианта конкретного ядра.[/uquote]Мы обсуждаем работу компилятора при создании кода для конкретного ядра CM4F (указанного в свойствах проекта). Компилятор имхо должен проводить оптимизацию под конкретное ядро, а не под коня в вакууме. А в CM4F все использованные команды (кроме LDR) - однотактные.
Добавлено after 15 minutes 29 seconds:
[uquote="Мурик",url="/forum/viewtopic.php?p=3741879#p3741879"]Результат GCC.[/uquote]Неправильно скомпилили.
Во-первых: Если посмотреть на тот код, что я приводил, видно что PIN_PRND_B - выделяет 0-й бит из некоего IO-порта, а PIN_PRND_F - 10-й бит из другого IO-порта. у Вас в обоих случаях выделяется 0-й бит. Поэтому компилятор выполнил оптимизацию, видимо поместив #1 в R1.
Во-вторых: Где-то до этого кода должна быть загрузка #1 в R1 (MOVS R1, #1) которую Вы не привели - а это ещё одна команда.
В-третьих: Странно что компилятор у Вас выполнил загрузку #1 в R1, а потом сделал AND с R1. В сумме это требует 3 команды, в то время как с константой #1 в коде команды это можно было сделать за 2 команды. Или у Вас включена оптимизация по размеру, а не по скорости, или выбрано неправильное ядро (не CM3, CM4F).
В-четвёртых: Компилятор у Вас зачем то перегружал повторно указатель, потратив на это лишние команды LDR. Возможно как-то неправильно объявлен указатель на IO-порт. Хотя это уже и не относится к вопросу....
Добавлено after 2 minutes 8 seconds:
[uquote="Shuspano",url="/forum/viewtopic.php?p=3742043#p3742043"]Да мне пока без разницы. Щас пока беспокоит вопрос: где и как узнать, что собственно писать, где описаны эти инструкции.[/uquote]Читать мануал на желаемый МК, мануал на ядро и посмотреть примеры программ для данного МК. Ну и подучить си конечно.
Добавлено after 15 minutes 29 seconds:
[uquote="Мурик",url="/forum/viewtopic.php?p=3741879#p3741879"]Результат GCC.[/uquote]Неправильно скомпилили.
Во-первых: Если посмотреть на тот код, что я приводил, видно что PIN_PRND_B - выделяет 0-й бит из некоего IO-порта, а PIN_PRND_F - 10-й бит из другого IO-порта. у Вас в обоих случаях выделяется 0-й бит. Поэтому компилятор выполнил оптимизацию, видимо поместив #1 в R1.
Во-вторых: Где-то до этого кода должна быть загрузка #1 в R1 (MOVS R1, #1) которую Вы не привели - а это ещё одна команда.
В-третьих: Странно что компилятор у Вас выполнил загрузку #1 в R1, а потом сделал AND с R1. В сумме это требует 3 команды, в то время как с константой #1 в коде команды это можно было сделать за 2 команды. Или у Вас включена оптимизация по размеру, а не по скорости, или выбрано неправильное ядро (не CM3, CM4F).
В-четвёртых: Компилятор у Вас зачем то перегружал повторно указатель, потратив на это лишние команды LDR. Возможно как-то неправильно объявлен указатель на IO-порт. Хотя это уже и не относится к вопросу....
Добавлено after 2 minutes 8 seconds:
[uquote="Shuspano",url="/forum/viewtopic.php?p=3742043#p3742043"]Да мне пока без разницы. Щас пока беспокоит вопрос: где и как узнать, что собственно писать, где описаны эти инструкции.[/uquote]Читать мануал на желаемый МК, мануал на ядро и посмотреть примеры программ для данного МК. Ну и подучить си конечно.
- Сообщения: 285
- Зарегистрирован: Вс сен 05, 2010 15:35:50
>> Читать мануал на желаемый МК, мануал на ядро и посмотреть примеры программ для данного МК. Ну и подучить си конечно.
Я так понял, человек не про это спрашивает. Интересует что-то, где хорошо описывалось бы программирование для STM со всеми его библиотеками и периферией.
Я так понял, человек не про это спрашивает. Интересует что-то, где хорошо описывалось бы программирование для STM со всеми его библиотеками и периферией.
- Сообщения: 270
- Зарегистрирован: Вс окт 20, 2019 13:03:56
Фух... вроде мигает. Да, от знания асма толку никакого вообще. Ну да ладно.
мне вот интересен еще момент. Почему если подпрограмму пишу где нить внизу листинга, а вызываю ее где нибудь вверху, например:
то транслятор ругается. Транслирует, но в итоге задержка не работает. А в асме это не разу не проблема.
мне вот интересен еще момент. Почему если подпрограмму пишу где нить внизу листинга, а вызываю ее где нибудь вверху, например:
Код: Выделить всё
while (1)
{
...
delay(8000000);
...
}
void delay(uint16_t time)
{}


