Недавно я обновил свой проект с gcc 4.3 до gcc 5.5.После этого я вижу изменение в поведении постинкрементного оператора, которое вызывает проблемы в моем проекте.Я использую глобальную переменную в качестве управляющей переменной.Например, рассмотрим этот пример программы:
int i = 0;
int main()
{
int x[10];
x[i++] = 5; ===> culprit
return 0;
}
В приведенном выше фрагменте значение i
следует увеличивать только после того, как 5
назначено x[0]
, что будет гарантировать, что x[0]
имеет правильное действительное значение, назначенное до увеличения i
.
Теперь возникает проблема, я вижу, что после перехода на gcc 5.5 инструкции по сборке изменились и значение i увеличивается на единицу еще до назначенияслучилось.Инструкция по сборке из приведенного выше фрагмента:
Dump of assembler code for function main():
6 {
0x0000000000400636 <+0>: push %rbp
0x0000000000400637 <+1>: mov %rsp,%rbp
7 int x[10];
8
9 x[i++] = 1;
0x000000000040063a <+4>: mov 0x200a00(%rip),%eax # 0x601040 <i>
0x0000000000400640 <+10>: lea 0x1(%rax),%edx
0x0000000000400643 <+13>: mov %edx,0x2009f7(%rip) # 0x601040 <i> ====> i gets incremented here
0x0000000000400649 <+19>: cltq
0x000000000040064b <+21>: movl $0x5,-0x30(%rbp,%rax,4) =====> x[0] is assigned value here
10
11 return 0;
0x0000000000400653 <+29>: mov $0x0,%eax
12
13 }
0x0000000000400658 <+34>: pop %rbp
0x0000000000400659 <+35>: retq
Из-за описанной выше сборки другой поток внутри процесса, использующий переменную i
, начинает читать неправильные значения из глобального массива.
Теперь тот же кодпри компиляции с использованием gcc 4.3 придерживается поведения, которое я понимаю, т.е. сначала присваивается значение, а затем i
увеличивается.Инструкция по сборке с использованием gcc 4.3 для того же фрагмента:
Dump of assembler code for function main():
5 int main()
0x00000000004005da <+0>: push %rbp
0x00000000004005db <+1>: mov %rsp,%rbp
6 {
7 int x[10];
8
9 x[i++] = 1;
0x00000000004005de <+4>: mov 0x200a64(%rip),%edx # 0x601048 <i>
0x00000000004005e4 <+10>: movslq %edx,%rax
0x00000000004005e7 <+13>: movl $0x5,-0x30(%rbp,%rax,4) ======> x[0] gets assigned here
0x00000000004005ef <+21>: lea 0x1(%rdx),%eax
0x00000000004005f2 <+24>: mov %eax,0x200a50(%rip) # 0x601048 <i> ======> i gets incremented here
10
11 return 0;
0x00000000004005f8 <+30>: mov $0x0,%eax
12
13 }
0x00000000004005fd <+35>: leaveq
0x00000000004005fe <+36>: retq
Я хочу знать, каково это ожидаемое поведение с новыми компиляторами?Есть ли переключатель, с помощью которого я могу вернуться к старому поведению?Или это ошибка в новом компиляторе?
Буду благодарен за любую помощь или подсказки.
Примечание. Я хочу избежать блокировок при чтении i
из-за проблем с производительностью.Строка culprit
в приведенном выше коде выполняется внутри блокировки.Таким образом, только один поток может обновить i
в любой момент, но из-за изменения инструкций по сборке внутри компиляторов условие гонки вводится без каких-либо изменений кода.
Редактировать 1: я знаю, что существуют проблемы с блокировкойи я также оставляю это в качестве опции, но на самом деле я хочу знать, есть ли какой-либо переключатель или флаг, с помощью которого я могу вернуться к старому поведению.Кодовая база очень большая, и мне пришлось бы пройтись по всей кодовой базе, чтобы проверить, существуют ли подобные проблемы в других местах кода.Итак, возвращение к старому поведению спасет жизнь.