Assuming the concern is size rather than speed (you don't want to drag in an 800 byte implementation of memcpy from libc), if only takes a couple of dozen instructions to implement all four routines.
I'm usually more annoyed at gcc NOT calling memcpy but using a completely unrolled copy of individual bytes when I told it to use memcpy.
https://godbolt.org/z/cbc8sW4M9
If code size matters, GCC apparently emits a tail call to memcpy when -Os is used.
The unrolled inline code has, of course, no call overhead, and is branch-free, avoiding branch misprediction issues.
Another drawback of calling an external memcpy function is that it is not possible to optimize for a constant length at compile time.

Maybe you didn't follow the godbolt link, above, but it shows calling libc memcpy with a constant length and gcc replaces the call to memcpy with inline straight line unrolled code.
This will happen even if you're using your own memcpy at link time, because gcc has no way of knowing you're going to do that. So, for example, you can't do logging "Memcpy called with src=... dst=... n=..."
I think you misunderstand me. Had I not followed the Godbolt link, I would not have responded with: 'The unrolled inline code has, of course, no call overhead and is branch-free, thus avoiding branch misprediction issues.'
OTOH, I just wanted to point out some drawbacks of emitting a call to memcpy instead of emitting inline code. The former happens, for example, when you replace -O with -Os in your Godbolt example, or if you add -fno-builtin-memcpy. What I wanted to say is, if a memcpy implementaion wants to distinguish different cases (e.g. byte-wise copy versus word-wise copy), it can only make this decision at runtime, while inline code can make some of these decisions at compile time, omitting the comparison instructions for constant conditions and omitting the dead code for the unreachable branches.
I also don't recall saying that the compiler would generate different code if I provided my own memcpy at link time. Of course it cannot know that in advance.
Note that if using the '-ffreestanding' compilation flag, it will emit a call to memcpy at any optimization level.
As bruceholt pointed out earlier, it's actually -fno-builtin, which prevents memcpy() from being replaced by __builtin_memcpy(). Whether __builtin_memcpy emits inline code or a call to memcpy depends on the optimization level
1). Since -ffreestanding also implies -fno-builtin, the same applies to -ffreestanding.
1) Edit: I think it's not only a matter of optimization level. __builtin_memcpy may generally fall back to emitting a call to memcpy in some cases, for example if the inline code would become too complex or too big.
You can't directly use the equivalent builtin function in freestanding mode
Not sure what you mean exactly. Your program can call __builtin_memcpy() in freestanding mode, and GCC emits either inline code or a call to memcpy, depending on optimization level. See
https://godbolt.org/z/na18z77r3