首页 > 解决方案 > 这种“省略失败”是语言规定的吗?

问题描述

考虑以下代码:

#include <utility>
#include <string>

int bar() {
    std::pair<int, std::string> p { 
        123, "Hey... no small-string optimization for me please!" };
    return p.first;
}

(简化感谢@Jarod42 :-) ...)

我希望该功能可以简单地实现:

bar():   
        mov eax, 123
        ret

但相反,实现调用,用我的文字operator new()构造一个,然后调用. 至少 - 这就是 gcc 9 和 clang 9 所做的()。这是铿锵声输出:std::stringoperator delete()GodBolt

bar():                                # @bar()
        push    rbx
        sub     rsp, 48
        mov     dword ptr [rsp + 8], 123
        lea     rax, [rsp + 32]
        mov     qword ptr [rsp + 16], rax
        mov     edi, 51
        call    operator new(unsigned long)
        mov     qword ptr [rsp + 16], rax
        mov     qword ptr [rsp + 32], 50
        movups  xmm0, xmmword ptr [rip + .L.str]
        movups  xmmword ptr [rax], xmm0
        movups  xmm0, xmmword ptr [rip + .L.str+16]
        movups  xmmword ptr [rax + 16], xmm0
        movups  xmm0, xmmword ptr [rip + .L.str+32]
        movups  xmmword ptr [rax + 32], xmm0
        mov     word ptr [rax + 48], 8549
        mov     qword ptr [rsp + 24], 50
        mov     byte ptr [rax + 50], 0
        mov     ebx, dword ptr [rsp + 8]
        mov     rdi, rax
        call    operator delete(void*)
        mov     eax, ebx
        add     rsp, 48
        pop     rbx
        ret
.L.str:
        .asciz  "Hey... no small-string optimization for me please!"

我的问题是:很明显,编译器对里面发生的一切都了如指掌bar()。为什么它不“删除”/优化字符串?进一步来说:

  1. 在基本级别上, thennew()和之间有代码delete(),编译器知道 AFAICT 不会产生任何有用的结果。
  2. 其次,new()anddelete()调用自己。毕竟,标准 AFAIK 允许小字符串优化,所以即使 clang/gcc 没有选择使用它 - 它可以;这意味着实际上不需要打电话new()或打电话delete()

我特别感兴趣的是,这其中的哪一部分直接归因于语言标准,哪一部分是编译器的非最优性。

标签: c++language-lawyercompiler-optimizationstdstringelision

解决方案


在此处的各种答案和评论中进行了讨论之后,我现在针对 GCC 和 LLVM 提交了以下关于此问题的错误:

  1. GCC 错误 94293[错过优化] new+delete of未删除的未使用的本地字符串

    最小测试用例 ( GodBolt):

    void foo() {
        int *p = new int[1];
        *p = 42;
        delete[] p;
    }
    
  2. GCC 错误 94294[错过优化] 填充本地字符串的无用语句未删除

    最小测试用例 ( GodBolt):

    void foo() {
        std::string s { "This is not a small string" };
    }
    
  3. LLVM 错误 45287[错过优化] 未能删除未使用的 libstdc++ std::string

    最小测试用例 ( GodBolt):

    void foo() {
        std::string s { "This is not a small string" };
    }
    

感谢:@JeffGarret、@NicolBolas、@Jarod42、Marc Glisse。

2021 年 8 月更新:使用最新版本的 clang++、g++ 和 libstc++,所有这些最小的测试用例都如预期的那样避开了内存分配。clang++ 对问题中的 OP 程序也有这种行为,但 GCC 仍然分配和解除分配。


推荐阅读