Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Странное предупреждение
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Страницы: 1, 2
Jenya7
Есть такая строчка в коде
Код
for (i=0; i < size_to_take; i++)
{
      DataBuffer[i] = (data_out[j++]<<8) | data_out[j++];
}
Выскакивает предупреждение
Цитата
Warning[Pa079]: undefined behavior: variable "j" (declared at line 176) (or a value reached by some form of indirection through it) is modified more than once without an intervening
В GCC компайлере такого предупреждения нет.Что надо переделать чтоб предупреждение исчезло?
scifi
j++ два раза в одном выражении - это бяка. Результат выражения не определён. Не надо так делать.
Jenya7
Цитата(scifi @ Jun 5 2018, 11:54) *
j++ два раза в одном выражении - это бяка. Результат выражения не определён. Не надо так делать.


у меня тут выбора особо нету sad.gif

если только так
Код
    DataBuffer[i] = (data_out[j++]<<8);
    DataBuffer[i] |=  data_out[j++];

по моему так еще уродливей
MrYuran
Цитата(scifi @ Jun 5 2018, 08:54) *
j++ два раза в одном выражении - это бяка. Результат выражения не определён. Не надо так делать.

Не результат выражения, а порядок операций.
а делать так действительно не надо.
Чем меньше неоднозначностей в коде, тем лучше.
Ещё рекомендую статический анализатор запускать
scifi
Цитата(Jenya7 @ Jun 5 2018, 08:58) *
по моему так еще уродливей

Что мешает сделать так:
Код
DataBuffer[i] = (data_out[j] << 8) | data_out[j + 1];
j += 2;
???
И дело не в уродливости, а в корректности. Оригинальный код некорректен, слава яру и его предупреждениям.

Цитата(MrYuran @ Jun 5 2018, 09:18) *
Не результат выражения, а порядок операций.

То есть вы утверждаете, что результат определён? Какое смелое заявление.
andrew_b
Цитата(scifi @ Jun 5 2018, 09:37) *
То есть вы утверждаете, что результат определён? Какое смелое заявление.
Какое смелое передёргивание.
Рискну предположить, что стандарт не говорит о результате. Он говорит о порядке операций.
MrYuran
Цитата(scifi @ Jun 5 2018, 09:37) *
То есть вы утверждаете, что результат определён? Какое смелое заявление.

В данном случае нет, но такое предупреждение бывает, например, при операциях с несколькими волатильными переменными
scifi
Цитата(andrew_b @ Jun 5 2018, 09:49) *
Рискну предположить, что стандарт не говорит о результате. Он говорит о порядке операций.

Неверное предположение. Конечно, стандарт говорит о результате, иначе зачем он нужен? Результат складывается из последовательности операций, и если порядок операций не определён, то и результат не определён. Очевидно же!

Цитата(MrYuran @ Jun 5 2018, 10:07) *
В данном случае нет, но такое предупреждение бывает, например, при операциях с несколькими волатильными переменными

Угу. beer.gif
ViKo
А вот же похожая тема. Только в ней инкремент с другой стороны относительно =
https://electronix.ru/forum/index.php?showt...650&hl=ViKo
Jenya7
Цитата(scifi @ Jun 5 2018, 12:37) *
Что мешает сделать так:
Код
DataBuffer[i] = (data_out[j] << 8) | data_out[j + 1];
j += 2;
???
И дело не в уродливости, а в корректности. Оригинальный код некорректен, слава яру и его предупреждениям.


То есть вы утверждаете, что результат определён? Какое смелое заявление.

на этом варианте остановлюсь. спасибо.
k155la3
Код
DataBuffer[i] = (data_out[j++]<<8) | data_out[j++];
похоже на пример из статей "Как писать неподдерживаемый код".
andrew_b
Цитата(scifi @ Jun 5 2018, 10:09) *
Неверное предположение. Конечно, стандарт говорит о результате, иначе зачем он нужен?

Цитата
The grouping of operators and operands is indicated by the syntax.71) Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
Оно?
Сергей Борщ
QUOTE (andrew_b @ Jun 5 2018, 16:24) *
Оно?
Оно самое.
Тему можно переименовать из "странное предупреждение" в "странная реакция на предупреждение" biggrin.gif
juvf
Цитата(Jenya7 @ Jun 5 2018, 10:52) *
Есть такая строчка в коде
Код
for (i=0; i < size_to_take; i++)
{
      DataBuffer[i] = (data_out[j++]<<8) | data_out[j++];
}

если нет перевёртывания с эндианами и DataBuffer - это 16-ти разрядное, то
Код
memcpy((void*)DataBuffer, (void*)&data_out[j], 2*size_to_take);
j += 2*size_to_take; //если j дальше не нужно, удалить эту строку
без ворнингов, безопаснее, и БЫСТРЕЕ. (явное преобразование к воид не обязательно).
scifi
Цитата(juvf @ Jun 20 2018, 10:19) *
без ворнингов, безопаснее, и БЫСТРЕЕ. (явное преобразование к воид не обязательно).

Большие и малые индейцы негодуэ crying.gif
jcxz
Цитата(juvf @ Jun 20 2018, 10:19) *
безопаснее, и БЫСТРЕЕ..

Не верю! cool.gif
juvf
Цитата(jcxz @ Jun 20 2018, 14:40) *
Не верю! cool.gif

Bера в церкви. Вы как гуру должны не верить, а знать, что for-loop будет работать не быстрее, чем любые практические реализации memcpy().
а про безопастность - так ТС стрельнул себе в ногу своим forech, причем даже где-то gcc не ругнулся. C memcpy не нужны манимуляции с j.
jcxz
Цитата(juvf @ Jun 20 2018, 13:10) *
Bера в церкви. Вы как гуру должны не верить, а знать, что for-loop будет работать не быстрее, чем любые практические реализации memcpy()[/url].

Я и знаю, что это не так. И знаю почему. И я знаю как внутри работает memcpy(). Поэтому он будет быстрее только в определённых случаях.
А в общем случае быстрее будет for loop. Особенно если вспомнить, что компиляторы умеют оптимизировать.

Цитата(juvf @ Jun 20 2018, 13:10) *
а про безопастность - так ТС стрельнул себе в ногу своим forech, причем даже где-то gcc не ругнулся. C memcpy не нужны манимуляции с j.

Не знаю, что Вы имеете в виду под "безопасностью", но даже Вы в своём совете с memcpy() допустили пару неточностей. laughing.gif
adnega
Цитата(juvf @ Jun 20 2018, 13:10) *
C memcpy не нужны манимуляции с j.

Увидеть бы весь код. Есть мнение, что j легким движением превратиться в (i * 2 + 0) и (i * 2 + 1).
juvf
Цитата(jcxz @ Jun 20 2018, 15:24) *
но даже Вы в своём совете с memcpy() допустили пару неточностей. laughing.gif
какую?

Цитата
Поэтому он будет быстрее только в определённых случаях.
в каких? поделитесь?
scifi
Цитата(juvf @ Jun 20 2018, 10:19) *
и БЫСТРЕЕ

Цитата(jcxz @ Jun 20 2018, 13:24) *
Поэтому он будет быстрее только в определённых случаях.

Интересно, куда все торопятся? Если судить по моему опыту в ымбеддед, ставить рекорды скорости приходится крайне редко smile3046.gif
VladislavS
Цитата(scifi @ Jun 20 2018, 15:52) *
Интересно, куда все торопятся?

Если я знаю несколько способов решить одну и ту же задачу, то естественно выберу тот который работает быстрее и/или занимает меньше ресурсов. Иначе будет как с "640K ought to be enough for anybody".

Цитата(scifi @ Jun 20 2018, 15:52) *
Если судить по моему опыту в ымбеддед, ставить рекорды скорости приходится крайне редко smile3046.gif

Именно потому что опыт это и есть набор эффективных реализаций, которые не тормозят там где не надо.
jcxz
Цитата(juvf @ Jun 20 2018, 13:37) *
какую?

Причём вообще в данном примере memcpy()? ...если ТС судя по всему пытается поменять байты местами при перемещении из источника в приёмник?
С чего Вы вообще взяли, что DataBuffer имеет размерность 16 бит, а data_out - 8 бит?
Причём там вообще memcpy()??? Научитесь читать исходники!

Цитата(scifi @ Jun 20 2018, 15:52) *
Интересно, куда все торопятся? Если судить по моему опыту в ымбеддед, ставить рекорды скорости приходится крайне редко smile3046.gif

И экономить электроэнергию - тоже редко? wacko.gif
adnega
Цитата(jcxz @ Jun 20 2018, 21:30) *
С чего Вы вообще взяли, что DataBuffer имеет размерность 16 бит, а data_out - 8 бит?

Это очевидно из строчки
DataBuffer[i] = (data_out[j++]<<8) | data_out[j++];
Если data_out будет больше 8 бит, то операция | будет перемешивать биты из двух соседних значений.
(data_out[j++]<<8) | data_out[j++] имеет в этом случае не более 16 значащих бит.
Соответственно DataBuffer не менее 16 бит. Какой смысл делать больше?
Obam
Цитата(juvf @ Jun 20 2018, 13:10) *
...ТС стрельнул себе в ногу своим forech, причем даже где-то gcc не ругнулся...

GCC не ругнётся нигде и никогда: у ТСа IAR wink.gif
jcxz
Цитата(adnega @ Jun 20 2018, 21:42) *
Это очевидно из строчки
DataBuffer[i] = (data_out[j++]<<8) | data_out[j++];

Уважаемый, из этой строчки очевидно только, что к некоему объекту DataBuffer применяются операции индексирования и присвоения значения, а к объекту data_out - операции индексирования и операции приведения к другому типу (возможно целочисленному).
Всё остальное - Ваши фантазии, не более. laughing.gif

Цитата(adnega @ Jun 20 2018, 21:42) *
Если data_out будет больше 8 бит, то операция | будет перемешивать биты из двух соседних значений.

И что? Может это и есть цель ТСа? laughing.gif

Цитата(adnega @ Jun 20 2018, 21:42) *
Соответственно DataBuffer не менее 16 бит. Какой смысл делать больше?

Я видимо ошибся форумом... Думал - тут форум программистов и электронщиков, а оказывается - гадалок и экстрасенсов. laughing.gif
Я, как можете заметить, не пытаюсь домысливать тайные намерения автора. И я понятия не имею - чего же он пытается изобразить.
Я просто основываюсь на том, что вижу в вопросе.
И всех возможных в мире задач я тоже не знаю - мало ли чего человек хочет? Может ему требуется читать из источника пары байт и записывать их по адресу назначения с расширением до 32-бит. Не задумывались?
Опять спросите "зачем"? Ну хотя бы например:
1. Многие МК имеют SPI-контроллеры, у которых передаваемое данное записывается в регистр, младшие 16 бит которого - собственно передаваемые биты, а старшие биты - различные биты управления (управление сигналами CS, управление межсловным интервалом и т.п.). Знаю несколько таких МК. Вот если нужно скажем передавать байты в SPI в том порядке, в котором они лежат в памяти, но передавать 16-битными словами (для уменьшения блока пересылки DMA) и при этом к словам добавить управляющую информацию, то может и потребоваться перевёртывание 16-битных слов с расширением их до 32 бит.
2. Или например - он готовит фрейм для передачи в видеопамять LCD-контроллера (опять-же по 16-битному SPI), который (контроллер) принимает пиксели с глубиной цвета 32 бита, а в памяти автора отрисовка идёт 16-битными пикселями. Соответственно - при передаче необходимо расширение каждого пикселя до 32 бит.
3. Или: у автора в устройстве на SPI висит пара слэйвов, соединённых в daisy-chain. Каждый из них имеет размер слова ==16 бит. И он хочет записывать в дальний в цепочке слэйв слова из data_out, а в ближний - нули. Ну скажем эти два слэйва - два 16-разрядных ЦАП, соединённых в daisy-chain. И его DMA должен по неким запросам выдавать по 32 бита на запрос (по слову в каждый ЦАП). Вот он и формирует буфер для DMA таким образом.
И это - всего три из тысяч возможных вариантов!
adnega
Цитата(jcxz @ Jun 20 2018, 22:31) *
И что? Может это и есть цель ТСа? laughing.gif

Ага. Причем это объекты классов, а операторы [] и | перегружены.
Бритва Оккама. Нет, не слышал.
Если бы было нужно что-то из описанного вами, то код бы выглядел иначе.
juvf
Цитата(Obam @ Jun 20 2018, 23:49) *
GCC не ругнётся нигде и никогда: у ТСа IAR wink.gif
2Obam, читаем ещё раз первый пост ТС до конца
Цитата
В GCC компайлере такого предупреждения нет.


Цитата
Причём вообще в данном примере memcpy()? ...если ТС судя по всему пытается поменять байты местами при перемещении из источника в приёмник?
С чего Вы вообще взяли, что DataBuffer имеет размерность 16 бит, а data_out - 8 бит?
Причём там вообще memcpy()??? Научитесь читать исходники!

adnega - ППКС,
2jcxz - из кода видно, что с вероятностью близкой к 100, данные из какого-то байтового тх/рх буфера перекидываются в нужный массив DataBuffer. Научитесь читать исходники!.
Но не факт что это так, поэтому я сразу написал
Цитата
если нет перевёртывания с эндианами и DataBuffer - это 16-ти разрядное, то

2jcxz - Научитесь читать посты участников.

Цитата
мало ли чего человек хочет? Может ему требуется читать из источника пары байт и записывать их по адресу назначения с расширением до 32-бит. Не задумывались?
Может быть! Согласен. Может data_out - это класс, а операторы << и | перегруженны. Может даже оператор "++" перегружен в "--". Может где то false переопределён в true. Но из кода видно, что с большей вероятностью просто перекачка из 8 бит массива в 16 битный, поэтому я и сделал оговорку.

Что касается memcpy() и остального....

Цитата
И я знаю как внутри работает memcpy()
Как вы можете знать, как работает memcpy() у ТС, если memcpy реализуется разработчиками каждого компилятора под каждую архитектуру? Вы знаете как каждый memcpy() реализован? У каждого своя реализация. Как правило, memcpy() реализован так, что из всех возможных реализаций код memcpy будет самый оптимальный, вплоть до ухода в асм. Если пользователь компилятора/библиотеки и решит написать свой копипаст, то он будет менее эффективный, в лучшем случае будет такойже, поэтому при копировании памяти не нужно замарачиваться и изобретать скоростной велосипед, а просто использовать memcpy(). Наверно бывают случаю, что memcpy сырой, написан индусами и можно свой написать оптимальней, но это на столько редко.... и нужно хорошо знать архитектуру. Потратите много времени, выиграете 1-2 такта на копировании 1 байта.

более того, если ЕСЛИ всё же нет перевертывания и это 8 и 16 бит, то даже плохой memcpy будет быстрее кода ТС. Посмотрите сколько лишних операций в for у ТС! какие-то приведения типов, операторы, << и |, дополнительный j, операции j++!!! УЖАС!!!(кстати.... j++ достаточно медленный, по сравнению с ++j). Более того, если ЕСЛИ data_out - это signed char или int8_t, то в коде ТС ошибка, которая обнаружиться только при выполнении, и хорошо если на столе ТС, а может и через год-два у пользователя.

jcxz я сюда не батлится с вами захожу, я тут черпаю чужой опыт, делюсь своим и чужим опытом. Поделитесь вы.....
Цитата
Поэтому он будет быстрее только в определённых случаях.

В каких случаях?
Цитата
Вы в своём совете с memcpy() допустили пару неточностей.
какие неточности? Опять же, mне на батл пофиг, просто я не хочу ошибаться и других вводить в заблуждение в отличие от некоторых
Или вы пустослов? Тогда можно нужно отфильтровать ваши посты, ибо это всё равно что "на заборе написано".

ps
вы сами себе противоречите, причем сразу в одном посте.....
Цитата
ТС судя по всему пытается поменять байты местами при перемещении из источника в приёмник...
С чего Вы вообще взяли, что DataBuffer имеет размерность 16 бит, а data_out - 8 бит?
так всё таки "ТС ... пытается поменять байты" или "С чего Вы вообще взяли, что ... data_out - 8 бит?"

С чего вы решили, что я утверждаю, что DataBuffer 16 бит, а data_out - 8 бит? Я писал ЕСЛИ, вы знаете значение слова "если"?
adnega
Цитата(juvf @ Jun 21 2018, 07:24) *
С чего вы решили, что я утверждаю, что DataBuffer 16 бит, а data_out - 8 бит? Я писал ЕСЛИ, вы знаете значение слова "если"?

Среди профессионалов принято так: если есть наиболее вероятное и простое объяснение, то нужно пользоваться именно им.
В противном случае один мой знакомый говорил: "а если инопланетяне прилетели и сделали {подставить нужное}".
Если ТС-инопланетянин, пишет, вроде, по нашему, но по ихнему это совсем другой смысл, то jcxz должен посыпать голову пеплом раз не
предположил, что такое возможно. Дальше смысл беседы в этом направлении теряется.
Если по исходнику понятно, что источник шире 8 байт приведет к перемешиванию бит, а в приемнике в итоге получается только 16 значащих бит,
то с уверенностью можно принять за факт их размеры 8 и 16 бит соответственно, и при необходимости заявлять "сам дурак",
если ТС начнет добавлять противоречащие новые обстоятельства.

В соседней ветке про таймеры ТС на третей станице пошел по второму кругу, хотя у меня была полная уверенность, что точка в вопросе поставлена.
В этой связи, jcxz поддерживаю, т.к. без телепатии, а на одной только логике, не понять, что нужно ТС.
Сергей Борщ
QUOTE (adnega @ Jun 21 2018, 08:32) *
"а если инопланетяне прилетели и сделали {подставить нужное}".
К сожалению, аборигены запомнили только, что нужно хлопать при приземлении.
jcxz
Цитата(juvf @ Jun 21 2018, 07:24) *
Как вы можете знать, как работает memcpy() у ТС, если memcpy реализуется разработчиками каждого компилятора под каждую архитектуру? Вы знаете как каждый memcpy() реализован? У каждого своя реализация.

"из кода видно, что с вероятностью близкой к 100..." где-то я это уже слышал недавно? wacko.gif
Так вот - у ТС-а IAR, и я им пользуюсь; у ТС-а (согласно строки выше) Cortex-M - и я под него сейчас отлаживаю. Аргументов достаточно?

Цитата(juvf @ Jun 21 2018, 07:24) *
Как правило, memcpy() реализован так, что из всех возможных реализаций код memcpy будет самый оптимальный, вплоть до ухода в асм. Если пользователь компилятора/библиотеки и решит написать свой копипаст, то он будет менее эффективный, в лучшем случае будет такойже, поэтому при копировании памяти не нужно замарачиваться и изобретать скоростной велосипед, а просто использовать memcpy().

С этим категорически не согласен! Попробуйте когда-нить не теоретизировать внустую, а скомпилировать и посмотреть разные варианты с копированием в цикле на разных компиляторах с включённой полной оптимизацией по скорости и с разными условиями цикла (короткий цикл/длинный, условия окончания и указатели - переменные или известны на этапе компиляции). И будете удивлены.
Вариант с memcpy() на коротких циклах, где кол-во проходов и/или указатели заданы переменными, проиграет варианту с простым for, так как ему не надо: a) сохранять содержимое из scratch регистров; б) делать вызов memcpy() и возврат; в) внутри memcpy() выполнять ветвление выбирая нужный вариант цикла копирования по условию длины цикла, выравниваний первого и второго аргумента и т.п.
На циклах с заранее (на этапе компиляции) известными условиями выполнения, for-вариант опять же будет как минимум не хуже, просто хотя-бы потому что компилятор зная условия выполнения цикла может подставить нужную последовательность команд (подобную оптимальному копированию внутри memcpy()), но с другими более удобными регистрами и их количеством - удобными для данной точки кода.
А некоторые компиляторы, так и вообще цикл for так оптимизируют (при определённых условиях) что готовому memcpy() и не снилось - в разы быстрее по скорости. Смотрим на компиляторы для DSP-ядер.

Цитата(juvf @ Jun 21 2018, 07:24) *
Посмотрите сколько лишних операций в for у ТС! какие-то приведения типов, операторы, << и |, дополнительный j, операции j++!!! УЖАС!!!

И что? Современные оптимизаторы творят чудеса. И им по-барабану Ваши жалкие потуги заменить операции индексирования на указатели и подобное - они это и сами хорошо делают, даже ещё и лучше. Так что если бы код автора не был такой кривой, результат работы оптимизирующего компилятора мог быть лучше memcpy().
Я в своё время, разрабатывая для DSP, немало заменил копирований через memcpy() на циклы for - это давало внушительный прирост скорости.

Цитата(juvf @ Jun 21 2018, 07:24) *
(кстати.... j++ достаточно медленный, по сравнению с ++j).

Опять чушь! Если предположить, что автор использует ARM, то что пре- что пост-инкремент - без разницы, и то и другое на ARM можно сделать в пределах одной команды выборки из памяти. Откройте мануал по системе команд и найдите разницу между: LDRB Rx, [Ry], #1 и LDRB Rx, [Ry, #1]!

Цитата(juvf @ Jun 21 2018, 07:24) *
В каких случаях? какие неточности?

Во-первых: memcpy там не к месту как я писал выше;
во-вторых: ничего не известно о значении size_to_take, а если она перед началом цикла может быть отрицательной (и i - тоже знаковое), что будет с вашим вариантом на memcpy()? biggrin.gif
в-3-х: с чего Вы приводите входные аргументы к типу void *? А если у ТС-а DataBuffer и data_out объявлены к примеру с модификатором volatile, т.е. указывают на strong ordered memory? Например DataBuffer или data_out - это FIFO-буфера в некоей периферии, чувствительные к размерности и последовательности операций записи/чтения.
Я уж не говорю о том, что эта память может позволять только доступы определённой разрядности...

Цитата(juvf @ Jun 21 2018, 07:24) *
вы сами себе противоречите, причем сразу в одном посте.....

В чём именно противоречие? wacko.gif

Цитата(adnega @ Jun 21 2018, 08:32) *
Если по исходнику понятно, что источник шире 8 байт приведет к перемешиванию бит, а в приемнике в итоге получается только 16 значащих бит,
то с уверенностью можно принять за факт их размеры 8 и 16 бит соответственно, и при необходимости заявлять "сам дурак",

Из "исходника" даже не ясно, что именно нужно этому горе-программисту (если data_out указывает на unsigned char) - поменять местами байты при копировании или может он хочет продублировать байты. Т.е. сделать так:
1. *(u16 *)&DataBuffer[i] = __REVSH(*(u16 __packed *)&data_out[j]); j += 2;
или так:
2. *(u16 *)&DataBuffer[i] = (uint)*(u8 *)&data_out[j++] * 0x101u;
а может и что другое.....
VladislavS
Цитата(Сергей Борщ @ Jun 21 2018, 09:22) *
К сожалению, аборигены запомнили только, что нужно хлопать при приземлении.

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

Цитата
Попробуйте когда-нить
попробую обязательно.

Цитата
Современные оптимизаторы творят чудеса. И им по-барабану Ваши жалкие потуги заменить операции индексирования на указатели и подобное - они это и сами хорошо делают, даже ещё и лучше.
Это я слышал и/или знаю.... Но, во первых, современные оптимизаторы иногда портят код так, что он не работает как надо, приходится либо отключать оптимизацию полностью, либо на отдельной функции. Во вторых про потуги вы зря.... когда мой код перестал влазить в флешь, а в прерываниях стал долго задерживаться, то сделал рефакторинг - код и влез и стал быстрее работать. Одна строчка до рефакторинга была 6 машинных команд, стала 2 или 4.
Убрал всякие лишние << |, на каждом участке экономил где по 10-20 байт флеша, где по 100.

Цитата
во-вторых: ничего не известно о значении size_to_take, а если она перед началом цикла может быть отрицательной (и i - тоже знаковое)

Морите!? О чем тут дискутировать, если вы не знаете как работает for? Если size_to_take отрицательная и i тоже знаковое, то в примере ТС в тело цикла for не разу не зайдем. Это вообще букварь Си.

Цитата
с чего Вы приводите входные аргументы к типу void *?
Да, согласен, неточность есть. Если быть скрупулёзным к явным преобразованиям, то нужно так
memcpy((void*)DataBuffer, (const void*)&data_out[j], 2*size_to_take);
Я не утверждаю, я всего лишь предположил, что копировать память лучше через memcpy. Если это не 8 и 16 бит, то забыли про мой совет. О чем спор?

Цитата
А если у ТС-а DataBuffer и data_out объявлены к примеру с модификатором volatile
И что? в чем разница вот в таких кодах с volatile (архитектура ст первый, без перевёртывания):
Вариант 1:
Код
volatile uint16_t DataBuffer[100];
volatile uint8_t data_out [1024];
uint16_t size_to_take = 100;
uint8_t j = 17;
for(uint16_t  i=0; i<size_to_take; i++ )
{
   DataBuffer[i] = (data_out[j] << 8)| data_out[j+1];
   j += 2;
}


Вариант 2:
Код
volatile uint16_t DataBuffer[100];
volatile uint8_t data_out [1024];
uint16_t size_to_take = 100;
memcpy((void*)DataBuffer, (const void*)&data_out[17], 2*size_to_take);


Или по вашему 1-ый вариант работать будет, а второй нет?



scifi
Цитата(juvf @ Jun 21 2018, 10:50) *
Но, во первых, современные оптимизаторы иногда портят код так, что он не работает как надо, приходится либо отключать оптимизацию полностью, либо на отдельной функции.

За такую ересь - пожизненный эцих с гвоздями maniac.gif
Подсказка: вы просто не умеете их готовить.
juvf
Цитата(scifi @ Jun 21 2018, 13:03) *
За такую ересь - пожизненный эцих с гвоздями maniac.gif
Я же говорю - тут церковь какая-то, пропитанная религией ))). Если вы с таким не встречались - это не значит, что такого нет.

ps Открою вам тайну, земля круглая!


jcxz
Цитата(juvf @ Jun 21 2018, 10:50) *
Морите!? О чем тут дискутировать, если вы не знаете как работает for? Если size_to_take отрицательная и i тоже знаковое, то в примере ТС в тело цикла for не разу не зайдем. Это вообще букварь Си.

и....? Продолжаем думать дальше. Подсказываю ещё раз: А что будет с Вашим вариантом на memcpy()?

Цитата(juvf @ Jun 21 2018, 10:50) *
И что? в чем разница вот в таких кодах с volatile (архитектура ст первый, без перевёртывания):
Или по вашему 1-ый вариант работать будет, а второй нет?

В каких-то случаях - да. Читайте мои посты внимательнее. Мы не знаем в какой памяти находятся исходный и целевой адреса. Возможно они находятся в памяти, требующей строго определённого порядка доступа и определённой разрядности доступа (strong ordered memory). Или в памяти при доступе к которой происходят прочие события (например FIFO-буфера периферии (SPI например) при записи/чтении данных в/из них производят другие действия: обмен с регистрами сдвига, удаление данных из FIFO и т.д.; а также чувствительны к разрядности операций чтения/записи - при доступе недопустимой разрядности получите fault; и чувствительны к выравниванию (хотя основная память МК может допуcкать невыровненный доступ, но некоторые регионы - могут генерить fault при невыровненном доступе)).
А какие именно операции доступа к памяти будут использованы внутри memcpy() - нет никакой гарантии. Если например DataBuffer указывает на 16-разрядный SPI-FIFO и size_to_take>=2, то внутри memcpy() может объединить доступы в 32-разрядные и в результате получим bus-fault.
Так что бездумно использовать memcpy() нельзя.
scifi
Цитата(juvf @ Jun 21 2018, 10:50) *
Или по вашему 1-ый вариант работать будет, а второй нет?

Тема больших и малых индейцев не раскрыта. А именно, у больших индейцев memcpy с успехом заменяет тот самый цикл копирования, а у малых - нет. ТС про индейцев ничего не говорил, отчего ваш $рачЪ на ровном месте выглядит особенно забавно laughing.gif
jcxz
Цитата(juvf @ Jun 21 2018, 10:50) *
Но, во первых, современные оптимизаторы иногда портят код так, что он не работает как надо, приходится либо отключать оптимизацию полностью

А вот здесь: код в студию!
Ибо в 99.9% таких случаев причина - баг в коде. smile3046.gif
Я даже скажу больше: я при отладке кода проверяю его работу с включенной и отключенной оптимизацией - это помогает находить скрытые баги в коде.
juvf
Цитата
Продолжаем думать дальше. Подсказываю ещё раз: А что будет с Вашим вариантом на memcpy()?
Что за глупость? при чем тут memcpy с отрицатеольными size_to_take? В коде ТС явно size_to_take > 0.
Цитата(jcxz @ Jun 21 2018, 13:44) *
А вот здесь: код в студию!

Код
#pragma optimize=none
unsigned int crc16_byte(unsigned int crc, unsigned int data)
{
    //const unsigned int Poly16=0xA001;
    unsigned int LSB;
    crc = ((crc^data) | 0xFF00) & (crc | 0x00FF);
    for (uint8_t i=0; i<8; i++)
    {
        LSB=(crc & 0x0001);
        crc >>= 1;
        if(LSB)
            crc=crc^0xA001;
    }
    return crc;
}
Такой код работает, если убрать прагму, то црц считается не правильно. Для фантазёров, видящих за рамками кода всякое ООП, перегрузку операторов, переопределение типов и прочую фуе ту.... перегрузки операторов нет, переопределений типов и чего либо ещё нет. тип unsigned int 16 бит!
adnega
Цитата(jcxz @ Jun 21 2018, 11:44) *
Ибо в 99.9% таких случаев причина - баг в коде. smile3046.gif

Да, но 0.1% все же баг в компиляторе.
У меня есть проект, который собирается и работает при O2 и не собирается при Os. Интересно?
Код
C:\Users\user\AppData\Local\Temp\ccShzzAY.s: Assembler messages:
C:\Users\user\AppData\Local\Temp\ccShzzAY.s:3151: Error: value of 256 too large for field of 1 bytes at 000000000000103a
make.EXE: *** [obj_sw/esp8266.o] Error 1

В asm
Код
.L276:
    .byte    (.L274-.L276)/2 - переполнение

Меняешь две строчки в Си-исходнике и проект начинает собираться.
scifi
Цитата(juvf @ Jun 21 2018, 12:01) *
Такой код работает, если убрать прагму, то црц считается не правильно. Для фантазёров, видящих за рамками кода всякое ООП, перегрузку операторов, переопределение типов и прочую фуе ту.... перегрузки операторов нет, переопределений типов и чего либо ещё нет. тип unsigned int 16 бит!

Не видно в этом коде никакого криминала. Конечно, нельзя исключить того, что криминал возникает вне этой функции (она же не в вакууме существует). Опять же, не исключён глюк компилятора. У меня яр для coldfire неправильно считал MD5 при включенной оптимизации. Поймать его за руку на конретной инструкции сложно, так как код MD5 зубодробительный.
juvf
Цитата(scifi @ Jun 21 2018, 14:07) *
Конечно, нельзя исключить того, что криминал возникает вне этой функции (она же не в вакууме существует).
Я тоже про это думал. На весь проект стоит максимальная оптимизация по размеру кода. Дебажил на уровне Си, в асм углубляться не стал. Одна строчка #pragma optimize=none давала правильную работу расчета црц. Поэтому врят-ли что-то в остальном коде не так. Времени не было искать глюк в компиляторе или ещё где.... Видимо это был 0,1%.

кому интересно, компилятор IAR C/C++ for STM8 из иара 3,10,1
jcxz
Цитата(juvf @ Jun 21 2018, 12:01) *
Что за глупость? при чем тут memcpy с отрицатеольными size_to_take? В коде ТС явно size_to_take > 0.

Где это "явно указано"? Ни объявления ни присвоения значения size_to_take там нету. А это означает, что size_to_take к началу цикла может принимать любое значение. И алгоритм вполне может быть рассчитан на то, что при size_to_take<=0 не должно выполняться ни одной итерации.
Глупость - это видеть то, чего нет.

Цитата(juvf @ Jun 21 2018, 12:01) *
Такой код работает, если убрать прагму, то црц считается не правильно.

В исходнике вроде криминала нет. Но это ничего не доказывает.
Без анализа ассемблерного результата ничего сказать нельзя.
Да даже анализ ассемблерного варианта может ничего не дать. Элементарная причина "неверной работы оптимизированного варианта" при работе оптимизированного например: переполнение стека в вызывающей задаче. Оптимизированные варианты, как правило расходуют больше стека (больше регистров используют, соответственно - больше их сохраняют на входе в функции; заменяют некоторые повторяющиеся куски кода на дополнительные подпрограммы, что опять же ведёт к увеличению расхода стека и т.п.), а в неоптимизированном варианте стека ещё хватает.
Другой вариант: при вкл. оптимизации функция может инлайниться в точки вызова, вызывая перераспределение регистров (увеличение их использования), что может приводить к проявлению скрытых багов в вызывающих функциях, которые при меньшем кол-ве используемых регистров не проявляются.
Так что наиболее вероятный диагноз остаётся тем же: с вероятностью 99.9% - баги в коде. laughing.gif И необязательно что в этой процедуре.

Цитата(adnega @ Jun 21 2018, 12:06) *
Да, но 0.1% все же баг в компиляторе.
У меня есть проект, который собирается и работает при O2 и не собирается при Os. Интересно?

В приведённом Вами примере баг в Вашем коде, а не в компиляторе. О чём компилятор Вам и сообщает.
Вы вычисляете выражение, результат которого зависит от взаимного расположения меток. Их взаимное расположение видимо определяется на этапе компиляции (в зависимости от результата компиляции кода, который между ними - размер его меняется). Естественно - когда результат не влазит в байт, компилятор Вам об этом сообщает. В чём его ошибка? wacko.gif
А писать так код - это выстрел себе в ногу: сегодня компилится, завтра - нет.

Цитата(juvf @ Jun 21 2018, 12:24) *
Я тоже про это думал. На весь проект стоит максимальная оптимизация по размеру кода. Дебажил на уровне Си, в асм углубляться не стал. Одна строчка #pragma optimize=none давала правильную работу расчета црц. Поэтому врят-ли что-то в остальном коде не так. Времени не было искать глюк в компиляторе или ещё где.... Видимо это был 0,1%.

Ну да - обычное оправдание багописателей в таких случаях: "не было времени искать баг, поэтому я закопал его поглубже". Это ничего, что потом, через неопределённой время он опять проявится и вдарит по лбу в самом неожиданном месте. Когда совершенно безобидный код станет глючить. И опять виноват конечно будет компилятор, ну кто-ж ещё! smile3009.gif

При отладке одно из главнейших правил: ПРИ ПРОЯВЛЕНИИ БАГА НЕ СЛЕДУЕТ СТАРАТЬСЯ ДОБИТЬСЯ ТОГО, ЧТОБЫ ОН НЕ ПРОЯВЛЯЛСЯ. НАДО НАОБОРОТ - ЗАКРЕПИТЬ УСЛОВИЯ, ПРИ КОТОРЫХ ОН ПРОЯВЛЯЕТСЯ, ДОБИТЬСЯ ЕГО ПОВТОРЯЕМОСТИ И ИСКАТЬ ЕГО ПРИЧИНУ И УСТРАНИТЬ! И пока этого не сделано - двигаться дальше нельзя.
Ведь не тот баг страшен, который чётко проявляется, а тот - что проявляется иногда, внезапно и хаотично.

Цитата(scifi @ Jun 21 2018, 12:07) *
Поймать его за руку на конретной инструкции сложно, так как код MD5 зубодробительный.

Один из вариантов действий в такой ситуации:
Скомпилить подозреваемую функцию с оптимизацией. Взять ассемблерный результат её компиляции, оформить в виде асм-функции. И вставить в исходник вместо вызова си-функции. Выключить оптимизацию и проверить работу кода. Если теперь глюк появился и в неоптимизированном варианте, то только в этом случае можно предполагать, что проблема в данной оптимизированной функции. Далее - только анализ этой ассемблерной функции.
juvf
Цитата(jcxz @ Jun 21 2018, 14:53) *
Где это "явно указано"?
Дальше в эту сторону дискутировать не выжу смысла. Учитесь читать исходный код.

Цитата
Так что наиболее вероятный диагноз остаётся тем же: с вероятностью 99.9% - баги в коде.
Если не на кофейной гуще гадать, а на картах Таро, то баг в ОС, на которой кросскомпиляция идет.

Вот ещё пример
Код
#define     __IO    volatile         /*!< defines 'read / write' permissions  */
typedef struct SPI_struct
{
  __IO uint8_t SR;     /*!< SPI status register */
}
SPI_TypeDef;
...
#define SPI1                        ((SPI_TypeDef *) SPI1_BASE)
SPI_FLAG_BSY    = (uint8_t)0x80
...





#pragma optimize=none //без прагмы waitSpi(); не дожидается выполнения
inline void waitSpi() {       while( (SPI1->SR & (uint8_t)SPI_FLAG_BSY));}
Тут без прагмы вход в waitSpi и мгновенный выход, не зависимо от флага SPI_FLAG_BSY.

Цитата
переполнение стека в вызывающей задаче.
ээээ.... а разве при переполнении стека программа аварийно не завершается? В моём случае с crc16_byte() идет подсчет црц, но на выходе он не вырный, падения программы нет. Програма продолжает работать, но бракует пакет по црц. Принятые данные не портятся во время работы crc16_byte() и во время работы программы в целом. Хотел подебажить crc16_byte(), но с оптимизацией код не шагается по строчками си. А без оптимизации шагается но и работает без ошибок.

Цитата
Да даже анализ ассемблерного варианта может ничего не дать. Элементарная причина "неверной работы оптимизированного варианта" при работе оптимизированного например: переполнение стека в вызывающей задаче.
Что-то я не пойму.... Вот, вы же сами утверждаете, что оптимизатор может сломать работу программы написанной без ошибок. Я о том же.
adnega
Цитата(jcxz @ Jun 21 2018, 13:09) *
В приведённом Вами примере баг в Вашем коде, а не в компиляторе. О чём компилятор Вам и сообщает.

Вот мой код. Это участок типа
Код
if(0){}
else if(состояние_1){сделать_1;}
else if(состояние_2){сделать_2;}

Так не собирается.
А так
Код
if(0){}
else if(состояние_2){сделать_2;}
else if(состояние_1){сделать_1;}

нет проблем.
Это чисто косяк компилятора.

Вот полный код:
CODE
//-------------------------------------------
// ESP8266_ST_UDP0_START
//- - - - - - - - - - - - - - - - - - - - - -
else if(esp8266_state == ESP8266_ST_UDP0_START)
{
if(esp8266_cmd_flag == ESP8266_CMD_START)
{
send_cmd("AT+CIPSTART=0,\"UDP\",\"255.255.255.255\",5002,5001,2");
}
if(esp8266_to > 1000)
{
SEND_TO;
esp8266_to_cmd(ESP8266_ST_IDLE);
}
}
//-------------------------------------------
// ESP8266_ST_UDP0_SEND
//- - - - - - - - - - - - - - - - - - - - - -
else if(esp8266_state == ESP8266_ST_UDP0_SEND)
{
if(esp8266_cmd_flag == ESP8266_CMD_START)
{
esp8266_cmd_flag = ESP8266_CMD_ST + 0;
esp8266_to = 0;
esp8266_last_cmd[0] = 0;
bl_export.bl_sp_str(&esp8266, "AT+CIPSEND=0,");
bl_export.bl_sp_dec(&esp8266, esp8266_udp_send_len);
bl_export.bl_sp_str(&esp8266, "\r\n");
bl_export.bl_sp_start(&esp8266);
}
if(esp8266_to > 1000)
{
SEND_TO;
esp8266_to_cmd(ESP8266_ST_IDLE);
}
}

Если соответствующие блоки ESP8266_ST_UDP0_START и ESP8266_ST_UDP0_SEND в исходнике переставить местами, то
компилятор генерит правильный s-файл. Иначе генерит таблицу, в которой смещения не помещаются.
Где тут моя ошибка? Где ошибка компилятора я разобрался и на всякий случай сменил компилятор (была сборка arm-kgp-eabi-procyon).
juvf
Цитата(jcxz @ Jun 21 2018, 15:18) *
При отладке одно из главнейших правил: ПРИ ПРОЯВЛЕНИИ БАГА НЕ СЛЕДУЕТ СТАРАТЬСЯ ДОБИТЬСЯ ТОГО, ЧТОБЫ ОН НЕ ПРОЯВЛЯЛСЯ. НАДО НАОБОРОТ - ЗАКРЕПИТЬ УСЛОВИЯ, ПРИ КОТОРЫХ ОН ПРОЯВЛЯЕТСЯ, ДОБИТЬСЯ ЕГО ПОВТОРЯЕМОСТИ И ИСКАТЬ ЕГО ПРИЧИНУ И УСТРАНИТЬ! И пока этого не сделано - двигаться дальше нельзя.
так я нашел причину и устранил. ))

inline void waitSpi() { while( (SPI1->SR & (uint8_t)SPI_FLAG_BSY));} - вот этот код не работает с оптимизацией в пустом хороводе. И не изредка - а всегда. 100 раз из 100. А без оптимизации работает как часы. ни каких сбоев в обмене по SPI замечено не было.

Цитата
Ведь не тот баг страшен, который чётко проявляется, а тот - что проявляется иногда, внезапно и хаотично.
У меня црц считается с ошибкой всегда, когда вкл оптимизация, не изредка, а всегда. И всегда считается правильно с отключенной оптимизацией. Нету хаотичных и внезапных сбоев.
Kabdim
Любопытства ради и самообразования для, не могли бы вы привести код црц и чем копилируете?
scifi
Цитата(juvf @ Jun 21 2018, 13:50) *
inline void waitSpi() { while( (SPI1->SR & (uint8_t)SPI_FLAG_BSY));} - вот этот код не работает с оптимизацией в пустом хороводе. И не изредка - а всегда. 100 раз из 100. А без оптимизации работает как часы. ни каких сбоев в обмене по SPI замечено не было.

Я так понимаю, версия про "volatile" была проверена и не подтвердилась? Опять же, конкретно в этом случае чтение листинга дизассемблера не затруднило бы нисколько. Из оправданий - только лень.
Кстати, бывают баги, зависящие от задержек. Скажем, регистр SPI1->SR нужно начинать читать не сразу, а через пару тактов, без оптимизации задержка получалась сама собой - как вариант.
juvf
Цитата(Kabdim @ Jun 21 2018, 16:02) *
Любопытства ради и самообразования для, не могли бы вы привести код црц и чем копилируете?

читайте выше. там и код и чем компилирую.
jcxz
Цитата(juvf @ Jun 21 2018, 13:24) *
ээээ.... а разве при переполнении стека программа аварийно не завершается?

При переполнении стека симптомы могут быть какие угодно: может fault вылететь, а могут просто данные испортиться.

Цитата(juvf @ Jun 21 2018, 13:24) *
А без оптимизации шагается но и работает без ошибок.

Я уже писал выше что можно сделать. Скомпилить данную функцию с оптимизацией. А потом вырезать её ассемблерный листинг и вставить в код компилируемый без оптимизации (как асм-функцию). И проверить работу кода теперь.
И по асму шагать не надо, надо выучить ассемблер и "глазами" аналитически сравнить си-исходник с асм-результатом. И только если в этом случае будут найдены несоответствия между ними - только в этом случае можно однозначно говорить о вине компилятора.

Цитата(juvf @ Jun 21 2018, 13:24) *
Что-то я не пойму.... Вот, вы же сами утверждаете, что оптимизатор может сломать работу программы написанной без ошибок. Я о том же.

Если Вы считаете, что в недостаточном объёме выделенной под стек памяти виноват оптимизатор, а не "пейсатель" кода, то больше разговаривать не о чем..... smile3046.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.