Почему MSVC оптимизирует этот вызов memcpy?

У меня есть следующий код C (я сократил его, удалив некоторые другие вызовы и проверки):

#include <stdint.h>
#include <memory.h>

extern char buffer[];

extern void getstr1(char *buff, int buflen);
extern void getstr2(char **s);
extern void dosomething(char *s);

void myfn()
{
    char *s, *s1;
    int len;

    getstr1(buffer, 128);
    getstr2(&s);

    len = *s + *buffer;
    memcpy(buffer + *buffer + 1, s + 1, (*s) * sizeof(char));
    *buffer = len;

    dosomething(buffer);
}

MSVC с опцией оптимизации /O2 выдает следующий вывод:

_s$ = -4                                                ; size = 4
void myfn(void) PROC                                 ; myfn, COMDAT
        push    ecx
        push    128                           ; 00000080H
        push    OFFSET char * buffer             ; buffer
        call    void getstr1(char *,int)           ; getstr1
        lea     eax, DWORD PTR _s$[esp+12]
        push    eax
        call    void getstr2(char * *)                    ; getstr2
        mov     eax, DWORD PTR _s$[esp+16]
        push    OFFSET char * buffer             ; buffer
        mov     al, BYTE PTR [eax]
        add     BYTE PTR char * buffer, al
        call    void dosomething(char *)              ; dosomething
        add     esp, 20                             ; 00000014H
        ret     0
void myfn(void) ENDP                                 ; myfn

Вы можете проверить это на Godbolt

Почему компилятор пропустил вызов memcpy? Интересно, что объявление внешней переменной как "extern char buffer [N];" где N> = 2 или как "extern char * buffer;" заставляет компилятор использовать memcpy. Также замена memcpy на memmove делает то же самое. Я знаю о возможном UB, когда области источника и назначения перекрываются, но здесь компилятор не знает об этом.

Ответ 1

Я думаю, что это ошибка в MSVC, поскольку то, что вы делаете, является законным.

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

Код, приведенный для воспроизведения проблемы в отчете об ошибке, также использует extern type array[];

По словам команды, эта проблема исправлена в следующем выпуске (который не упоминается).

Ответ 2

То, что вы делаете, совершенно законно, это определенно ошибка в MSVC.

Вот урезанная версия для сообщения об ошибке:

#include <string.h>

extern unsigned char buffer[], *s;

void myfn() {
    memcpy(buffer + *buffer + 1, s + 1, *s);
    *buffer = 1;
}

Компилируется в:

void myfn(void) PROC                                 ; myfn, COMDAT
        mov     BYTE PTR unsigned char * buffer, 1
        ret     0
void myfn(void) ENDP                                 ; myfn

Удаление оператора *buffer = 1; исправляет ошибку генерации кода.
Проверьте это на проводнике компилятора Godbolt.