Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does MSVC optimize away this memcpy call?

Tags:

c++

c

I have the following C code (I shortened it removing some other calls and checks):

#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 with the /O2 optimization option produces the following output:

_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

You can check this on Godbolt

Why did the compiler omit the memcpy call? It's interesting that declaring the external variable as "extern char buffer[N];" where N >= 2 or as "extern char *buffer;" makes the compiler use memcpy. Also replacing memcpy with memmove does the same thing. I know about possible UB when the source and destination regions overlap but here the compiler doesn't have knowledge of this.

like image 737
oleque Avatar asked Mar 28 '19 13:03

oleque


2 Answers

I think this is a bug in MSVC as what you are doing is legal.

Note that there has been a similar bug filed already titled: Release build with speed optimize leaves an array uninitialized.

The code given to reproduce the problem in the bug report also uses an extern type array[];

As per the team, this issue is fixed in an upcoming release (which is not mentioned).

like image 56
P.W Avatar answered Nov 18 '22 20:11

P.W


What you do is perfectly legal, this is definitely a bug in MSVC.

Here is a stripped down version to file a bug report:

#include <string.h>

extern unsigned char buffer[], *s;

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

Compiles to:

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

Removing the statement *buffer = 1; prevents the code generation bug.
Check it on Godbolt's Compiler Explorer.

like image 2
chqrlie Avatar answered Nov 18 '22 20:11

chqrlie