When printing static, format-less strings, one of the common optimizations that C compilers perform is to transform calls like printf("foobar\n"); to the equivalent puts("foobar");. This is valid as long as the return value is not used (C specifies that printf returns the number of characters written on success, but that puts only returns a non-negative value on success). C compilers will also transform calls like fprintf(stdout, "foobar") to fwrite("foobar", 1, 6, stdout).
However, the printf to puts optimization only applies if the string ends with a newline, since puts automatically appends a newline. When it does not, I would expect that printf could be optimized to an equivalent fwrite, just like the fprintf case - but it seems like compilers don't do this. For example, the following code (Godbolt link):
#include <stdio.h>
int main() {
printf("test1\n");
printf("test2");
fprintf(stdout, "test3");
}
gets optimized to the following sequence of calls in assembly:
puts("test1");
printf("test2");
fwrite("test3", 1, 5, stdout);
My question is: why do compilers not optimize printf to fwrite or similar in the absence of a terminating newline? Is this simply a missed optimization, or is there a semantic difference between printf and fwrite when used with static, format-less strings? If relevant, I am looking for answers that would apply to C11 or any newer standard.