libc-hls is a reimplementation of many libc functions to enable High-level Synthesis (HLS) compatibility. The library is generated on-the-fly for a given HLS kernel, tailored to the libc functions called by it. We divide and transform calls to libc as follows:
- User-space calls (i.e., calls that perform no system calls)
- Calls with variadic arguments (e.g.,
sprintf(char *str, const char *format, ...)): we replace each call with a specialized implementation tailored to its arguments. For instance, we transformsprintf(buf, "%d %f", anInt, aFloat)to a call tohls_sprintf_df(char *str, const char *format, int arg1, float arg2). - Calls with function pointers (e.g.,
qsort(buf, count, size, comparator)) are replaced with calls to implementations that inline the function pointed by the pointer, as long as it can be solved in compile-time. - Calls to non-reentrant and stateful functions, such as
rand()orstrtok(), are replaced by local implementations that preserve their state in HLS. - Calls that are already synthesizable, e.g.,
sqrt()ormemcpy(), are replaced by wrappers, e.g.,hls_sqrt()andmemcpy(). By default, these wrappers simply call the original function, but may be used to provide custom implementations of these functions if the versions provided by the HLS tool are not satisfactory or need to be otherwise changed.
- Calls with variadic arguments (e.g.,
- Kernel-space calls (i.e., calls that perform system calls, or have side effects outside the program)
- Calls with no return values (e.g.,
exit()) and whose return values are commonly ignored (e.g.,printf()) are replaced by an asynchronous "fire-and-forget" mechanism, where the HLS kernel triggers the call but keeps running without checking it if finishes executing. In turn, the CPU host asynchronously receives a message to execute the system call, alongside its arguments. This assures that every no-return system call is eventually executed. This process requires boilerplate code on both the kernel and the host, and is generated automatically.
- Calls with no return values (e.g.,
We do not support any kernel-space calls with relevant return values, e.g., malloc() or fopen(), as we do not yet have a mechanism to make the kernel wait for the result of a host call, nor a way to map host-space pointers to kernel-space after the kernel starts executing. This extends to tacitly user-space calls, such as strdup(), which, while not wrapping any system call, still use dynamic memory allocations internally.