В C, если B является летучим, следует ли выражение (void) (B = 1) читать B

Я работаю над компиляторами для нескольких встроенных платформ. Пользователь недавно жаловался на следующее поведение одного из наших компиляторов. Данный код выглядит следующим образом:

extern volatile int MY_REGISTER;

void Test(void)
{
    (void) (MY_REGISTER = 1);
}

Компилятор генерирует это (в псевдоассемблере):

Test:
    move regA, 1
    store regA, MY_REGISTER
    load regB, MY_REGISER

То есть он не только записывает MY_REGISTER, но и считывает его обратно. Дополнительная нагрузка нарушила его по соображениям производительности. Я объяснил, что это произошло из-за того, что согласно стандарту "Выражение присваивания имеет значение левого операнда после присваивания, [...]".

Как ни странно, удаление изменения отбрасывания к void изменяет поведение: загрузка исчезает. Пользователь счастлив, но я просто смущен.

Поэтому я также проверил это в нескольких версиях GCC (3.3 и 4.4). Там компилятор никогда не генерирует нагрузку, даже если значение явно используется, например.

int TestTwo(void)
{
    return (MY_REGISTER = 1);
}

Входит в

TestTwo:
    move regA, 1
    store regA, MY_REGISTER
    move returnValue, 1
    return

Есть ли у кого-нибудь представление о том, что является правильной интерпретацией стандарта? Должна ли произойти перезагрузка? Правильно или полезно добавить чтение только в том случае, если значение используется или применяется для void?

Ответы

Ответ 1

Соответствующий параграф в стандарте - это

Оператор присваивания сохраняет значение в объекте, указанном слева операнд. Выражение присваивания значение левого операнда после задание, но не является значением lvalue. Тип выражения присваивания является типом левого операнда, если только левый операнд имеет квалифицированный тип, в этом случае это неквалифицированный версия типа левой операнд. Побочный эффект обновления сохраненного значения левого операнда должен между предыдущей и следующей точками последовательности.

Таким образом, это явно делает разницу между "значением левого операнда" и обновлением сохраненного значения. Также обратите внимание, что возврат не является lvalue (поэтому нет ссылки на переменную в возврате выражения), и все квалификаторы теряются.

Итак, я прочитал это как gcc, делая правильную вещь, когда он возвращает значение, которое он сознательно должен хранить.

Edit:

Ближайший стандарт планирует уточнить, добавив сноску:

Реализация разрешена прочитайте объект, чтобы определить значение но не требуется, даже если объект имеет изменчивый тип.

Изменить 2:

На самом деле есть еще один параграф о выражениях, которые могут пролить свет на это:

Выражение в выражении утверждение оценивается как пустота выражение для его побочных эффектов. \footnote {tакие как назначения и вызовы функций, которые имеют побочные эффекты}

Так как это означает, что эффект возврата значения не нужен для такого оператора, это настоятельно предполагает, что значение может быть загружено только из переменной, если значение используется.

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

Ответ 2

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

volatile вообще не очень хорошо определен - "Что представляет собой доступ к объекту, который имеет изменчивый тип, определяемый реализацией ".

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

(void) v;

обязательно должен быть одним, и, учитывая это, у меня нет причин для

(void) v = exp;

не быть. Но в любом случае я бы дал предупреждение, объясняющее, как получить другой эффект.

BTW. Если вы работаете над компилятором, возможно, у вас есть кто-то, кто находится в контакте с комитетом C, заполнение официального отчета о дефекте приведет к вам, если вы будете иметь обязательную интерпретацию (ну, есть риск, что DR будет классифицирован "Not A Defect" "без всякого намека на то, что они хотят...)

Ответ 3

Язык в стандарте ничего не говорит о чтении изменчивой переменной, только то, что значение выражения присваивания, которое а) определено семантикой С, а не содержимым переменной и б) здесь не используется, поэтому не нужно рассчитывать.