Встроенная сборка GCC - чем отличаются __volatile__ и "память"?
Во встроенной сборке GCC существует два способа предотвращения оптимизации: __volatile__
ключевое слово и вставка "memory"
в список клоббер-регистров.
Мой вопрос в чем разница с __volatile__
а также "memory"
- Кажется, они одинаковы... Однако сегодня я столкнулся со странной ситуацией, которая показывает, что они определенно отличаются! (У моей программы была ошибка в функциях ввода / вывода порта, когда я использовал "memory"
, но это становится хорошо, когда я использовал __volatile__
.)
Какая разница?
2 ответа
Мое чтение документации GCC заключается в том, что __volatile__
Ключевое слово для сборки, которая имеет побочные эффекты: то есть она делает что-то иное, чем вывод данных из выходных данных. В вашем случае, я полагаю, что "функции ввода / вывода порта" могут вызвать побочные эффекты.
"memory"
Clobber только для сборки, которая читает / записывает память, отличную от операндов ввода / вывода. Хотя это побочный эффект, не все побочные эффекты связаны с памятью.
Из руководства:
Типичное использование расширенных asm-операторов - манипулирование входными значениями для получения выходных значений. Тем не менее, ваши asm заявления также могут вызывать побочные эффекты. Если это так, вам может потребоваться использовать изменяемый квалификатор для отключения определенных оптимизаций.
а также
Clobber "памяти" сообщает компилятору, что код сборки выполняет чтение или запись в память для элементов, отличных от перечисленных в операндах ввода и вывода (например, для доступа к памяти, на которую указывает один из входных параметров).
С помощью __volatile__
Вы гарантируете, что значение всегда извлекается из ОЗУ, минуя кэш ЦП. Это, как указано в ответе Майкла Роусона, приводит к побочным эффектам, но в том смысле, что обычная оптимизация с помощью кэша ЦП "отключена" и ничего более.
В вашем случае значение, считанное с порта ввода / вывода (и сохраненное в переменной), может быть обновлено быстрее, чем недействительность кэша ЦП, поэтому вы можете прочитать "старое" значение. С помощью __volatile__
Вы всегда читаете не кэшированное значение.
Вы также можете увидеть: этот пост (я не знаю, если ваша архитектура ARM, но концепция та же).