I am trying to write some bare metal code with a memset
-style loop in it:
for (int i = 0; i < N; ++i) {
arr[i] = 0;
}
It is compiled with GCC and GCC is smart enough to turn that into a call to memset()
. Unfortunately because it's bare metal I have no memset()
(normally in libc) so I get a link error.
undefined reference to `memset'
It seems like the optimisation that does this transformation is -ftree-loop-distribute-patterns
:
Perform loop distribution of patterns that can be code generated with calls to a library. This flag is enabled by default at -O2 and higher, and by
-fprofile-use
and-fauto-profile
.
So one person's solution was to just lower the optimisation level. Not very satisfying.
I also found this really helpful page that explains that -ffreestanding
is not enough to get GCC not to do this, and there's basically no option but to provide your own implementations of memcpy
, memmove
, memset
and memcmp
. I'm happy to do that, but how?
If I just write memset
the compiler will detect the loop inside it and transform it into a call to memset! In fact in the code provided by the CPU vendor I'm using I actually found this comment:
/*
// This is commented out because the assembly code that the compiler generates appears to be
// wrong. The code would recursively call the memset function and eventually overruns the
// stack space.
void * memset(void *dest, int ch, size_t count)
...
So I assume that is the issue they ran into.
How do I supply a C implementation of memset
without the compiler optimising it to a call to itself and without disabling that optimisation?
Aha I checked in the glibc code and there's a inhibit_loop_to_libcall
modifier which sounds like it should do this. It is defined like this:
/* Add the compiler optimization to inhibit loop transformation to library
calls. This is used to avoid recursive calls in memset and memmove
default implementations. */
#ifdef HAVE_CC_INHIBIT_LOOP_TO_LIBCALL
# define inhibit_loop_to_libcall \
__attribute__ ((__optimize__ ("-fno-tree-loop-distribute-patterns")))
#else
# define inhibit_loop_to_libcall
#endif