web-dev-qa-db-fra.com

Le compilateur arrête d'optimiser la chaîne inutilisée lors de l'ajout de caractères

Je suis curieux de savoir pourquoi le morceau de code suivant:

#include <string>
int main()
{
    std::string a = "ABCDEFGHIJKLMNO";
}

une fois compilé avec -O3 donne le code suivant:

main:                                   # @main
    xor     eax, eax
    ret

(Je comprends parfaitement qu'il n'y a pas besoin de a inutilisé pour que le compilateur puisse entièrement l'omettre du code généré)

Cependant le programme suivant:

#include <string>
int main()
{
    std::string a = "ABCDEFGHIJKLMNOP"; // <-- !!! One Extra P 
}

rendements:

main:                                   # @main
        Push    rbx
        sub     rsp, 48
        lea     rbx, [rsp + 32]
        mov     qword ptr [rsp + 16], rbx
        mov     qword ptr [rsp + 8], 16
        lea     rdi, [rsp + 16]
        lea     rsi, [rsp + 8]
        xor     edx, edx
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)
        mov     qword ptr [rsp + 16], rax
        mov     rcx, qword ptr [rsp + 8]
        mov     qword ptr [rsp + 32], rcx
        movups  xmm0, xmmword ptr [rip + .L.str]
        movups  xmmword ptr [rax], xmm0
        mov     qword ptr [rsp + 24], rcx
        mov     rax, qword ptr [rsp + 16]
        mov     byte ptr [rax + rcx], 0
        mov     rdi, qword ptr [rsp + 16]
        cmp     rdi, rbx
        je      .LBB0_3
        call    operator delete(void*)
.LBB0_3:
        xor     eax, eax
        add     rsp, 48
        pop     rbx
        ret
        mov     rdi, rax
        call    _Unwind_Resume
.L.str:
        .asciz  "ABCDEFGHIJKLMNOP"

lorsqu'il est compilé avec le même -O3. Je ne comprends pas pourquoi il ne reconnaît pas que le a est toujours inutilisé, même si la chaîne fait un octet de plus.

Cette question est pertinente pour gcc 9.1 et clang 8.0, (en ligne: https://gcc.godbolt.org/z/p1Z8Ns ) car d'autres compilateurs dans mon observation suppriment entièrement la variable inutilisée (ellcc) ou générer du code pour cela quelle que soit la longueur de la chaîne.

70
Ferenc Deak

Bien que la réponse acceptée soit valide, depuis C++ 14, il est en fait vrai que les appels new et delete peuvent être optimisé loin. Voir cette formulation obscure sur cppreference:

Les nouvelles expressions sont autorisées à éliminer ... les allocations effectuées via des fonctions d'allocation remplaçables. En cas d'élision, le stockage peut être fourni par le compilateur sans faire appel à une fonction d'allocation (cela permet également d'optimiser la nouvelle expression inutilisée).

...

Notez que cette optimisation n'est autorisée que lorsque de nouvelles expressions sont utilisées, pas d'autres méthodes pour appeler une fonction d'allocation remplaçable: delete[] new int[10]; Peut être optimisé, mais l'opérateur delete(operator new(10)); ne peut pas.

Cela permet en fait aux compilateurs de supprimer complètement votre std::string Local même s'il est très long. En fait - clang ++ avec libc ++ fait déjà cela (GodBolt), car libc ++ utilise les inserts __new Et __delete Dans son implémentation de std::string - c'est "le stockage fourni par le compilateur". Ainsi, nous obtenons:

main():
        xor eax, eax
        ret

avec une chaîne inutilisée de n'importe quelle longueur.

GCC ne fait pas mais j'ai récemment ouvert des rapports de bogues à ce sujet; voir this SO answer pour les liens.

0
einpoklum