Разрешено ли это преобразование компилятора?
Рассмотрим этот код, где x
а также y
целые числа:
if (x)
y = 42;
Разрешено ли следующее преобразование компилятора?
int tmp = y;
y = 42;
if (!x)
y = tmp;
контекст:
Это из часто задаваемых вопросов Бьярна Страуструпа:
// start with x==0 and y==0
if (x) y = 1; // Thread 1
if (y) x = 1; // Thread 2
Часто задаваемые вопросы утверждают, что это гонка данных бесплатно; с x
а также y
оба 0, ни один из переменных не должен быть записан.
Но что, если трансформация разрешена?
1 ответ
В отличие от того, что я написал в своем некорректном комментарии, это преобразование фактически не допускается, если y
потенциально разделяется между потоками, и компилятор не может доказать существование существующего UB в исходном коде.
Стандарт прямо говорит:
Преобразования компилятора, которые вводят назначения в потенциально разделяемую область памяти, которая не будет изменена абстрактной машиной, обычно исключаются этим стандартом, поскольку такое назначение может перезаписывать другое назначение другим потоком в случаях, когда выполнение абстрактной машины не имело бы столкнулся с гонкой данных.
[intro.multithread] (1.10 / 22) в N3337, (1.10/25) в N4141.
Так что если x
всегда равно 0, исходный код будет свободен от гонки, а преобразованный - нет. Таким образом, преобразование не является законным.
Если бы это было так, то вы просто не смогли бы исключить доступ к любому объекту, достижимому из глобальных переменных или других переменных. Компилятор может даже предварительно вызывать функции, которые никогда не вызываются, всякий раз, когда делается косвенный вызов, и "отменять" их действие впоследствии, восстанавливая исходное значение.
Пройдя по пути оптимальной пессимизации, он может заранее сделать деление на ноль, а затем "проигнорировать" результат, если делитель был равен нулю, даже если это ловушка и программа остановлена.
Это явно абсурдно и должно быть отвергнуто независимо от того, должно ли это быть согласно стандарту.