Приветствую.
После последней type aliasing темы, решил я добить вопрос до конца и исследовать поведение оптимизатора когда он имеет дело с volatile аргументами. Ведь volatile должен запретить выполнять различные оптимизации над переменной и заставить не копить её в регистре. С такими убеждениями я набросал три функции и компилировал их в различных сочетаниях:
void f111111111(volatile int *i, short *s) {
for(int cnt = 0; cnt < 5; ++ cnt) {
*i += *s;
s += 2;
}
}
// аналогична первой, но i volatile
void f111111111(int *i, short *s) {
for(int cnt = 0; cnt < 5; ++ cnt) {
*i += *s;
s += 2;
}
}
int main() {
int ar[5] = {1, 2, 3, 4, 5};
f111111111(&ar[3], (short*)ar);
cout << ar[3] << endl;
}
У меня gcc 5.4.0 (gcc 6.1 показывает аналогичное поведение). Ожидал следующего выхлопа от cout: если оптимизатор поработал, то 19; если нет, то 25. Вот что получилось:
// main() и void f111111111() в разных модулях; -O2,
// i не volatile; f111111111() не заинлайнина; cout: 19;
// работает правильно
_Z10f111111111PiPs:
// void f111111111(int *i, short *s)
movl 12(%esp), %esi // esi = i
movl 16(%esp), %eax // eax = s
movl (%esi), %edx // edx = *i
leal 20(%eax), %ebx // ebx = &ar[5]
.L2:
movswl (%eax), %ecx // ecx = *eax
addl $4, %eax // eax += 4 байта
addl %ecx, %edx // edx += ecx
cmpl %ebx, %eax
jne .L2 // if(eax != ebx) goto .L2
movl %edx, (%esi) // *i = edx (*i = 19)
// main() и void f111111111() в разных модулях; -O2;
// i volatile; f111111111() не заинлайнина; cout: 25;
// работает правильно
_Z10f111111111PViPs:
// void f111111111(volatile int *i, short *s)
movl 16(%esp), %eax // eax = s
movl 12(%esp), %ecx // ecx = i
leal 20(%eax), %esi // esi = &ar[5]
.L2:
movswl (%eax), %ebx // ebx = *eax
movl (%ecx), %edx // edx = *ecx
addl $4, %eax // eax += 4 байта
addl %ebx, %edx // edx += ebx
cmpl %esi, %eax
movl %edx, (%ecx) // *i = edx
jne .L2 // if(esi != eax) goto .L2
// main() и void f111111111() в одном модуле; -O2;
// i volatile; f111111111() инлайнится; cout: случайное число;
// ошибка
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %ecx
leal -12(%ebp), %ebx // ebx = &ar[5]
subl $32, %esp
movl $4, -20(%ebp)
movl %gs:20, %eax
movl %eax, -12(%ebp)
xorl %eax, %eax
leal -32(%ebp), %eax // eax = &ar[0]
.L7:
movswl (%eax), %ecx // ecx = *eax
movl -20(%ebp), %edx // edx = *i
addl $4, %eax // eax += 4 байта
addl %ecx, %edx // edx += ecx
cmpl %ebx, %eax
movl %edx, -20(%ebp) // *i = edx
jne .L7 // if(ebx != eax) goto .L7
Считаю, что в 3 случаи gcc показал наличие у него бага (cout выдаёт случайное число, например 32670). Поковырялся под оталдчиком, ar[5] не проинициализирован (содержит случайные значения), но сам цикл вполне корректен. clang в третьем случаи выдаёт 25 - всё правильно.
Господа, если согласны с тем, что это баг, то может вызовется кто-нибудь сообщить куда следует (товарищам из gcc)? Описывать суть на английском для меня трудновато будет, читать умею, а говорить нет.