<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div><b>TL;DR:</b></div><div>Defining memory functions in C / C++ results in a chicken and egg problem. Clang can mutate the code into semantically equivalent calls to libc. None of `-fno-builtin-memcpy`, `-ffreestanding` nor `-nostdlib` provide a satisfactory answer to the problem.<br></div><div><br></div><div><div><b>Goal</b></div><div>Create libc's memory functions (aka `memcpy`, `memset`, `memcmp`, ...) in C++ to benefit from compiler's knowledge and profile guided optimizations.</div></div><div><br></div><div><div><b>Current state</b></div><div>LLVM is allowed to replace a piece of code that looks like a memcpy with an IR intrinsic that implements the same semantic, namely `call void @llvm.memcpy.p0i8.p0i8.i64` (e.g. <a href="https://godbolt.org/z/0y1Yqh" target="_blank">https://godbolt.org/z/0y1Yqh</a>).</div><div><br></div><div>This is a problem when designing a libc's memory function as the compiler may choose to replace the implementation with a call to itself (e.g. <a href="https://godbolt.org/z/eg0p_E" target="_blank">https://godbolt.org/z/eg0p_E</a>)</div><div><br></div><div>Using `-fno-builtin-memcpy` prevents the compiler from understanding that an expression has memory copy semantic, effectively removing `@llvm.memcpy` at the IR level : <a href="https://godbolt.org/z/lnCIIh" target="_blank">https://godbolt.org/z/lnCIIh</a>. In this specific example, the vectorizer kicks in and the generated code is quite good. Unfortunately this is not always the case: <a href="https://godbolt.org/z/mHpAYe" target="_blank">https://godbolt.org/z/mHpAYe</a>.</div><div><br></div><div>In addition `-fno-builtin-memcpy` prevents the compiler from understanding that a piece of code has the memory copy semantic but does not prevent the compiler from generating calls to libc's `memcpy`, for instance:</div><div>Using `__builtin_memcpy`: <a href="https://godbolt.org/z/O0sjIl" target="_blank">https://godbolt.org/z/O0sjIl</a></div><div>Passing big structs by value: <a href="https://godbolt.org/z/4BUDc0" target="_blank">https://godbolt.org/z/4BUDc0</a></div><div><br></div><div>In both cases, the generated `@llvm.memcpy` IR intrinsic is lowered into a libc `memcpy` call.</div><div><br></div><div>We would like to use `__builtin_memcpy` to communicate the semantic to the compiler but prevent it from generating calls to the libc.</div><div><br></div><div>One could argue that this is the purpose of `-ffreestanding` but the standard leaves a lot of freestanding requirements implementation defined ( see <a href="https://en.cppreference.com/w/cpp/freestanding" target="_blank">https://en.cppreference.com/w/cpp/freestanding</a> ).</div><div><br></div><div>In practice, making sure that `-ffreestanding` never calls libc memory functions will probably do more harm than good. People using `-ffreestanding` are now expecting the compiler to call these functions, inlining bloat can be problematic for the embedded world ( see comments in <a href="https://reviews.llvm.org/D60719" target="_blank">https://reviews.llvm.org/D60719</a> )</div></div><div><br></div><div><div><b>Proposals</b></div><div>We envision two approaches: an <b>attribute to prevent the compiler from synthesizing calls</b> or a <b>set of builtins</b> to communicate the intent more precisely to the compiler.</div><div><br></div><div>  1. A function/module attribute to disable synthesis of calls</div><div><br></div><div>    1.1 A specific attribute to disable the synthesis of a single call</div><div><font face="monospace, monospace">__attribute__((disable_call_synthesis("memcpy")))</font></div><div>Question: Is it possible to specify the attribute several times on a function to disable many calls?</div><div><br></div><div>    1.2 A specific attribute to disable synthesis of all libc calls</div><div><font face="monospace, monospace">__attribute__((disable_libc_call_synthesis))</font></div><div>With this one we are losing precision and we may inline too much. There is also the question of what is considered a libc function, LLVM mainly defines target library calls.</div><div><br></div><div>    1.3 Stretch - a specific attribute to redirect a single synthesizable function.</div><div>This one would help explore the impact of replacing a synthesized function call with another function but is not strictly required to solve the problem at hand.</div><div><font face="monospace, monospace">__attribute__((redirect_synthesized_calls("memcpy", "my_memcpy")))</font></div><div><br></div><div>  2. A set of builtins in clang to communicate the intent clearly</div><div><br></div><div><font face="monospace, monospace">__builtin_memcpy_alwaysinline(...)</font></div><div><font face="monospace, monospace">__builtin_memmove_alwaysinline(...)</font></div><div><font face="monospace, monospace">__builtin_memset_alwaysinline(...)</font></div><div><br></div><div>To achieve this we may have to provide new IR builtins (e.g. `@llvm.alwaysinline_memcpy`) which can be a lot of work.</div></div></div></div></div></div></div>