首页 > 解决方案 > 为什么 C++ 编译器内联字符串文字的一部分?

问题描述

考虑这个小的 C++ 代码片段:

#include <iostream>
#include <string>

int main() {
    std::cout << std::string("This.String.Ends!") << std::endl;
}

此片段生成的程序集的一部分(使用 clang++ -O3 编译):

...

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     byte ptr [rax + 16], 33           <--------- !!!!!!!!!
mov     qword ptr [rsp + 24], rcx
mov     rax, qword ptr [rsp + 16]
mov     byte ptr [rax + rcx], 0

...

.L.str:
        .asciz  "This.String.Ends!"

即使字符串文字'!'末尾有字符,生成的程序集也有一个额外的指令来显式添加它。问题:

  1. 这种优化叫什么?它有正式的名字吗?
  2. 我能够用 size 字符串重现这种行为8x+y。我可以想象从内存中仅获取一个字符比使用附加指令要昂贵。这里是这样吗?如果是这样,为什么不内联整个字符串(在这种情况下它很短)?
  3. 我仍然可以保留-O3但避免这种特殊优化的不同方式是什么?从命中和试验中,我可以发现使用这些(与g++)的组合会禁用它:-fno-tree-ccp -fno-tree-dominator-opts -fno-tree-forwprop -fno-tree-fre -fno-code-hoisting -fno-tree-pre -fno-tree-vrp,但我猜每个人都会做更多我可能不想错过的事情。
  4. 如果编译器确实为最后一个字符生成指令,为什么还要将完整的字符串留在.rodata二进制部分,而不仅仅是开始的 16 个字节?不浪费空间吗?

我的用例:这种优化在编译+链接后的二进制文件中修补字符串文字会产生问题(替换'x''y'填充\0*(len('x')-len('y')),假设len('x') >= len('y'))。我知道可能会有更多此类优化无法让我实现这一目标,但我只是想提供一些关于我如何解决这个问题的背景信息。

标签: c++g++compiler-optimizationclang++

解决方案


推荐阅读