Violate volatiles

31 январь, 2009 - 14:11Андрей Зубинский

Сколько лет языку С? Порядка 39. Даже по человеческим меркам – прощай молодость. И вроде как стандарты совершенствуются, а у реализаций до сих пор зрелость не наступает, шалят они иногда. Причем шалят там, где культурным солидным почти сорокалетним дядям шалить уже вовсе непозволительно.


Читаем стандарт языка С, ISO/IEC 9899:1999, раздел 6.7.3 «Квалификаторы типов», параграф 6: «Объект, тип которого квалифицирован как volatile, может быть изменен неизвестными для разработчика компилятора способами и иметь другие побочные эффекты. Поэтому любое выражение с участием таких объектов должно вычисляться строго в соответствии с правилами (в оригинале - shall be evaluated strictly according to the rules) абстрактной машины, как описано в 5.1.2.3».

Очень хорошо, читаем 5.1.2.3 (раздел «Окружение», параграф «Исполнение программ»): «Доступ к объектам, квалифицированным как volatile и модификации их, модификации файлов или вызов функций, которые выполняют эти действия, порождают побочные эффекты, которые являются изменениями в состоянии среды исполнения. Вычисление таких выражений должно порождать побочные явления».

Итак, если с переменной, квалифицированной программистом как volatile, выполняется цепочка операций, по мнению разработчика компилятора (или его оптимизатора кода) не изменяющая состояние переменной , эта цепочка операций должна быть выполнена. Всегда. Безоговорочно.

И это не потешное правило.

70% проектов встраиваемых систем в 2008 году – это язык C.

А встраиваемые системы – это ведь не только милый mp3-плеер, набитый мелодичной чепухой, это ведь ещё и кардиостимуляторы, например. И противоюзовые-противозаносные системы автомобилей. И ещё бог весть что.

И если программист обозначил какую переменную volatile, то он что-то знал.

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

Например, чтение из volatile-квалифицированной ячейки памяти, за которой на самом деле «прячется» какой-нибудь хитрый управляющий регистр , может иметь весьма специфические побочные эффекты – скажем, где-то в каком-то устройстве из-за этого инициируется новый аппаратный процесс, приводящий к бог весть чему, вплоть до аппаратного рестарта работы всего вычислителя (обычное дело, кстати, называется watchdog – программа периодически «почитывает» регистрик, сообщая специальному механизму, что она ещё работает в нормальном режиме, и если периодичность этих чтений будет нарушена, механизм просто сгенерирует сигнал аппаратного перезапуска, или сброса, потому что программа наверняка зациклилась или впала в анабиоз).

В конце прошлого года в Университете штата Юта проверили  тринадцать С-компиляторов на правильность «понимания» квалификатора volatile. Были открыты веселые вещи. Вот, например, фрагмент совершенно законного С-кода, в котором переменная Flag  квалифицирована как volatile:

Flag = Flag ;

 

Согласно требованиям стандарта, для этого кода должны быть сгенерированы машинные команды, в точности делающие то, чего хотел программист – из ячейки с адресом, где хранится переменная Flag  должны быть прочтены данные, и они же должны быть записаны в эту же ячейку. То есть, процессор должен непременно выполнить как минимум две машинные команды.

А вот GCC с генератором кода для микроконтроллеров семейства MSP430 (эти микроконтроллеры, а не эту версию GCC, выпускает Texas Instruments)  для такой очевидной операции выдаёт ничтожное ничто.

То есть вообще ничего не выдаёт.

Никакого кода. 

Да и большинство кросс-компиляторов для самых разных целевых процессоров на основе GCC третьей версии поступают так же шаловливо. Больше того, и другие компиляторы «отметились» тем, что хоть пару ошибок при работе с volatile, да допускали.

Как пишут девочки в своих уютных онлайновых дневничках, мну грусняво.

39 лет языку, причём одному из самых простых высокоуровневых, а компиляторы до сих пор с подводными камнями, да ещё и, по закону подлости, натыканными в самых опасных местах. 

 

ЗЫ

Пока брился, сформулировал вывод из всего этого.

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