Я работаю над компиляторами для нескольких встроенных платформ. Пользователь недавно жаловался на следующее поведение одного из наших компиляторов. Данный код выглядит следующим образом:
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?