Why aren't those function calls optimized?

Viewed 199

I've tried compiling this code both with Clang and GCC:

struct s { int _[50]; };

void (*pF)(const struct s), (*pF1)(struct s), (*pF2)(struct s *);

main()
{
    struct s a;

    pF2(&a);

    pF(a), pF1(a);
}

The result is the same. Although the call to pF isn't allowed to modify its only argument, the object a is copied for the second call to pF1. Why is this?

Here is the assembly output (from GCC):

; main

        push    rbx
        sub rsp, 0D0h
        mov rbx, rsp
        mov rdi, rsp
        call    cs:pF2

    ;create argument for pF1 call (as there the argument is modified)
    ;and copy the local a into it
    ;although it seems not needed because the local isn't futher read anyway

        sub rsp, 0D0h
        mov rsi, rbx
        mov ecx, 19h
        mov rdi, rsp
    ;

        rep movsq
        call    cs:pF

    ;copy the local a into the argument created once again
    ;though the argument cannot be modified by the function pointed by pF

        mov rdi, rsp
        mov rsi, rbx
        mov ecx, 19h
        rep movsq
    ;

        call    cs:pF1
        add rsp, 1A0h
        xor eax, eax
        pop rbx
        retn

Can't the optimizer see that as the function pointed by pF can't modify its parameter (as it's declared const) and so omit the last copying operation? Also, recently I saw that, as the variable a isn't further read in the code, it could use its storage for the function arguments.

The same code can be written as:

; main

            push    rbx
            sub rsp, 0D0h

            mov rdi, rsp
            call    cs:pF2

            call    cs:pF

            call    cs:pF1
            add rsp, 0D0h
            xor eax, eax
            pop rbx
            retn

I'm compiling with -O3 flag. Am I missing something?

It's the same even if I don't invoke UB (as the functions pointers are by default NULL) and I instead initialize them like:

#include <stdio.h>

struct s { int _[50]; };

extern void f2(struct s *a);

void (*pF)(const struct s), (*pF1)(struct s), (*pF2)(struct s *) = f2;

extern void f1(struct s a)
{
    a._[2] = 90;
}

extern void f(const struct s a)
{
    for(size_t i = 0; i < sizeof(a._)/sizeof(a._[0]); ++i)
        printf("%d\n", a._[i]);
}

extern void f2(struct s *a)
{
    a->_[6] = 90;

    pF1 = f1, pF = f;
}
2 Answers
Related