为什么 `printf("%s", "foo")` 没有被优化为 `fputs("foo", stdout)`?

Why is `printf("%s", "foo")` not being optimized to `fputs("foo", stdout)`?

所以 GCC 和 Clang 都足够聪明,可以将 printf("%s\n", "foo") 优化为 puts("foo") (GCC, Clang)。太好了。

但是当我运行这个函数通过Compiler Explorer:

#include <stdio.h>

void foo(void) {
    printf("%s", "foo");
}

GCC 和 Clang 都没有将 printf("%s", "foo") 优化为 fputs("foo", stdout),我认为这应该是相同的(因为 fputs 不会像 puts 那样放置换行符) printf 更快。

x86-64 GCC 11.1 (link):

.LC0:
        .string "foo"
.LC1:
        .string "%s"
foo:
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:.LC1
        xor     eax, eax
        jmp     printf

x86-64 Clang 12.0.0 (link):

foo:                                    # @foo
        mov     edi, offset .L.str
        mov     esi, offset .L.str.1
        xor     eax, eax
        jmp     printf                          # TAILCALL
.L.str:
        .asciz  "%s"

.L.str.1:
        .asciz  "foo"

有什么具体原因没有对 fputs 进行优化,还是编译器不够智能?

一些非常具体的情况得到了优化,就像你展示的那样,但它非常肤浅,如果你在你的格式字符串中添加一些东西,即使是 space,它 immediately discards the puts and goes back to printf.

我想没有什么可以阻止更广泛的优化,我的推测是,由于性能提升不是那么大,进一步添加更多 special cases 被认为因为不值得。

根据我的推测,缺少 fputs 优化属于 不值得 类别。

这个旧的 gcc printf optimization document 阐明了这些优化,我怀疑今天它会大不相同。

具体来说:

2.3 %s\n

A printf call with the format string %s\n [line 4679-4687] is converted to a puts() call.

 printf("%s\n", "hello world"); // converted to puts("hello world");

2.8 The string not ending with \n

No optimization if the string is not ending with \n [line 4721-4739].

2.9 The string ending with \n

A printf call with a simple format string ending with \n [line 4721-4739] is converted to a puts() call.

 printf("hello world\n"); // converted to puts("hello world");