diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b414f23091ce..555a54fd51a8 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9854,9 +9854,9 @@ not all aspects of the implementation are needed for a given call, the compiler may redirect the call to the identifier given as the first argument to the attribute (the modular implementation function). -The second argument is a implementation name, and the remaining arguments are +The second argument is an implementation name, and the remaining arguments are aspects of the format string for the compiler to report. The implementation -name is an unevaluated identifier be in the C namespace. +name is an unevaluated identifier in the C namespace. The compiler reports that a call requires an aspect by issuing a relocation for the symbol ``_`` at the point of the call. This arranges for diff --git a/libc/config/config.json b/libc/config/config.json index 603fa005fcc5..2ab09f6717f2 100644 --- a/libc/config/config.json +++ b/libc/config/config.json @@ -59,6 +59,10 @@ "LIBC_COPT_PRINTF_DISABLE_BITINT": { "value": false, "doc": "Disable bitint length modifiers to reduce code size. Specifically the wNUM and wfNUM modifiers." + }, + "LIBC_CONF_PRINTF_MODULAR": { + "value": false, + "doc": "Split printf implementation into modules that can be lazily linked in." } }, "scanf": { diff --git a/libc/docs/dev/index.rst b/libc/docs/dev/index.rst index c0085e63cbad..615db030d7ed 100644 --- a/libc/docs/dev/index.rst +++ b/libc/docs/dev/index.rst @@ -22,3 +22,4 @@ Navigate to the links below for information on the respective topics: undefined_behavior printf_behavior syscall_wrapper_refactor + modular_format diff --git a/libc/docs/dev/modular_format.rst b/libc/docs/dev/modular_format.rst new file mode 100644 index 000000000000..f5852c07678f --- /dev/null +++ b/libc/docs/dev/modular_format.rst @@ -0,0 +1,68 @@ +.. _modular_format: + +====================== +Modular format strings +====================== + +Introduction +============ + +Several C standard library functions (most notably, ``printf`` and ``scanf``), +present a large amount of related features to the caller configured via a +format string. This benefits code size at the caller, since format strings are +typically quite dense, and the equivalent of many individual calls can be +performed with only one. Overall this is a benefit, since to a function calls +typically outnumber the one definition of that function. + +However, the implementations of various libc features gated behind aspects of +those format strings can be large enough that they completely swamp the +programs that call them. Floating point and errno conversion in particular can +involve large tables which may be wholly dead. However, due to the format +string structure, this code is dead in a way previously invisible to the +compiler. + +To address this, an clang attribute was introduced: ``modular_format(, +, ...)``. This adds to the semantics of the existing +``format`` attribute (which must also be present, if implicitly.) The first +argument is a symbol naming a modular version of the implementation; this +version only weakly refers to "aspects" of the implementation that may not be +necessary for certain format strings. The second argument is general +"implementation name" string, and the remaining arguments are a list of handled +aspects of the format string. When the compiler sees that a given call only +needs a fixed set of aspects of the implementation, it may redirect the call to +the implementation function and emit a series of relocations to symbols named +``_``. These in turn bring the needed aspects of the call +into the link. The default entrypoints fall the modular ones, except they bring +in every possible implementation aspect. + +Mechanism +========= + +This functionality is currently gated behind ``LIBC_COPT_PRINTF_MODULAR``. When +set, the ``printf``-family functions gain modular variants, and the regular +variants are modified to call them and emit NONE relocations against all +implementation aspects. + +The implementation aspects are defined in headers using the +``LIBC_PRINTF_MODULE((), { })`` macro. If +``LIBC_COPT_PRINTF_MODULAR`` is not defined, then this macro makes these +``LIBC_INLINE`` definitions as per usual. Otherwise, for normal usage, these +become weak declarations, which causes any references to the module to become +weak. The implementations are moved to a dedicated impl file for groups of +modules. These define the aspect symbol and the module impls by defining +``LIBC_PRINTF_DEFINE_MODULES`` before including the header. This causes the to +be brought into the link exactly when the aspect symbol is referenced. + +Template functions present a special complication: the implementation must +instantiate them for any value that may be used. Since the purpose of the +templates is to implement a fixed interface, the possible arguments should +always be fixed and finite. Accordingly, libc contains def files to enumerate +possible arguments and provide handling for each. Templates are instantiated in +the headers whenever ``LIBC_PRINTF_DEFINE_MODULES`` is defined. + +libc and the compiler may understand different sets of aspect names, but their +understanding of what an aspect name means must be identical. libc reports the +set of aspect names that it needs a verdict on, and the compiler will only +provide a verdict for those aspects. If libc asks for a verdict on an aspect +unknown to the compiler, the aspect must be summarily considered to be +required. diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index e0e2e058da85..cc0b83124d06 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -360,6 +360,7 @@ add_header_macro( ../libc/include/stdio.yaml stdio.h DEPENDS + .llvm-libc-macros._LIBC_MODULAR_FORMAT_PRINTF .llvm-libc-macros.file_seek_macros .llvm-libc-macros.null_macro .llvm-libc-macros.stdio_macros diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt index 6a91b394a878..1f34257c57e0 100644 --- a/libc/include/llvm-libc-macros/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/CMakeLists.txt @@ -3,15 +3,19 @@ function(add_macro_header name) cmake_parse_arguments( "MACRO_HEADER" "" # Optional arguments - "HDR" # Single value arguments + "HDR;DEST_HDR" # Single value arguments "DEPENDS" # Multi-value arguments ${ARGN} ) + if (MACRO_HEADER_DEST_HDR) + set(dest_header_arg DEST_HDR ${MACRO_HEADER_DEST_HDR}) + endif() if(TARGET libc.include.llvm-libc-macros.${LIBC_TARGET_OS}.${name}) add_header( ${name} HDR ${MACRO_HEADER_HDR} + ${dest_header_arg} DEPENDS .${LIBC_TARGET_OS}.${name} ${MACRO_HEADER_DEPENDS} @@ -21,6 +25,7 @@ function(add_macro_header name) ${name} HDR ${MACRO_HEADER_HDR} + ${dest_header_arg} DEPENDS ${MACRO_HEADER_DEPENDS} ) @@ -395,3 +400,18 @@ add_macro_header( sysexits-macros.h ) +if (LIBC_CONF_MODULAR_FORMAT) + add_macro_header( + _LIBC_MODULAR_FORMAT_PRINTF + HDR + _LIBC_MODULAR_FORMAT_PRINTF.h + ) +else() + add_macro_header( + _LIBC_MODULAR_FORMAT_PRINTF + HDR + _LIBC_MODULAR_FORMAT_PRINTF-disable.h + DEST_HDR + _LIBC_MODULAR_FORMAT_PRINTF.h + ) +endif() diff --git a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h new file mode 100644 index 000000000000..e3238161b380 --- /dev/null +++ b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h @@ -0,0 +1,14 @@ +//===-- Definition to disable modular format macro for printf -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H +#define LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H + +#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN) + +#endif // LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H diff --git a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h new file mode 100644 index 000000000000..918241ab8f2e --- /dev/null +++ b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h @@ -0,0 +1,15 @@ +//===-- Definition of modular format macro for printf ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H +#define LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H + +#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN) \ + __attribute__((modular_format(MODULAR_IMPL_FN, "__printf", "float"))) + +#endif // LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H diff --git a/libc/include/stdio.yaml b/libc/include/stdio.yaml index c50b4ecb0bf0..67eeeaf07bdd 100644 --- a/libc/include/stdio.yaml +++ b/libc/include/stdio.yaml @@ -31,6 +31,8 @@ functions: - type: char **__restrict - type: const char *__restrict - type: '...' + attributes: + - _LIBC_MODULAR_FORMAT_PRINTF(__asprintf_modular) - name: clearerr standards: - stdc @@ -276,6 +278,8 @@ functions: arguments: - type: const char *__restrict - type: '...' + attributes: + - _LIBC_MODULAR_FORMAT_PRINTF(__printf_modular) - name: putc standards: - stdc @@ -347,6 +351,8 @@ functions: - type: size_t - type: const char *__restrict - type: '...' + attributes: + - _LIBC_MODULAR_FORMAT_PRINTF(__snprintf_modular) - name: sprintf standards: - stdc @@ -355,6 +361,8 @@ functions: - type: char *__restrict - type: const char *__restrict - type: '...' + attributes: + - _LIBC_MODULAR_FORMAT_PRINTF(__sprintf_modular) - name: sscanf standards: - stdc @@ -378,6 +386,8 @@ functions: - type: char **__restrict - type: const char *__restrict - type: va_list + attributes: + - _LIBC_MODULAR_FORMAT_PRINTF(__vasprintf_modular) - name: vfprintf standards: - stdc @@ -393,6 +403,8 @@ functions: arguments: - type: const char *__restrict - type: va_list + attributes: + - _LIBC_MODULAR_FORMAT_PRINTF(__vprintf_modular) - name: vsnprintf standards: - stdc @@ -402,6 +414,8 @@ functions: - type: size_t - type: const char *__restrict - type: va_list + attributes: + - _LIBC_MODULAR_FORMAT_PRINTF(__vsnprintf_modular) - name: vsprintf standards: - stdc @@ -410,6 +424,8 @@ functions: - type: char *__restrict - type: const char *__restrict - type: va_list + attributes: + - _LIBC_MODULAR_FORMAT_PRINTF(__vsprintf_modular) - name: vsscanf standards: - stdc diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt index c75c8b11be2b..9486a5349979 100644 --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -116,10 +116,15 @@ add_entrypoint_object( libc.src.stdio.scanf_core.string_reader ) +set(sprintf_srcs sprintf.cpp) +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND sprintf_srcs sprintf_modular.cpp) +endif() + add_entrypoint_object( sprintf SRCS - sprintf.cpp + ${sprintf_srcs} HDRS sprintf.h DEPENDS @@ -131,10 +136,14 @@ add_entrypoint_object( libc.src.__support.CPP.limits ) +set(snprintf_srcs snprintf.cpp) +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND snprintf_srcs snprintf_modular.cpp) +endif() add_entrypoint_object( snprintf SRCS - snprintf.cpp + ${snprintf_srcs} HDRS snprintf.h DEPENDS @@ -146,10 +155,14 @@ add_entrypoint_object( libc.src.__support.CPP.limits ) +set(asprintf_srcs asprintf.cpp) +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND asprintf_srcs asprintf_modular.cpp) +endif() add_entrypoint_object( asprintf SRCS - asprintf.cpp + ${asprintf_srcs} HDRS asprintf.h DEPENDS @@ -160,10 +173,15 @@ add_entrypoint_object( libc.src.__support.CPP.limits ) +set(vsprintf_srcs vsprintf.cpp) +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND vsprintf_srcs vsprintf_modular.cpp) +endif() + add_entrypoint_object( vsprintf SRCS - vsprintf.cpp + ${vsprintf_srcs} HDRS vsprintf.h DEPENDS @@ -175,10 +193,15 @@ add_entrypoint_object( libc.src.__support.CPP.limits ) +set(vsnprintf_srcs vsnprintf.cpp) +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND vsnprintf_srcs vsnprintf_modular.cpp) +endif() + add_entrypoint_object( vsnprintf SRCS - vsnprintf.cpp + ${vsnprintf_srcs} HDRS vsnprintf.h DEPENDS @@ -190,10 +213,15 @@ add_entrypoint_object( libc.src.__support.CPP.limits ) +set(vasprintf_srcs vasprintf.cpp) +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND vasprintf_srcs vasprintf_modular.cpp) +endif() + add_entrypoint_object( vasprintf SRCS - vasprintf.cpp + ${vasprintf_srcs} HDRS vasprintf.h DEPENDS diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp index 0991dfca6a05..f80d2a5eb759 100644 --- a/libc/src/stdio/asprintf.cpp +++ b/libc/src/stdio/asprintf.cpp @@ -26,7 +26,12 @@ LLVM_LIBC_FUNCTION(int, asprintf, // and pointer semantics, as well as handling // destruction automatically. va_end(vlist); +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + auto ret_val = printf_core::vasprintf_internal(buffer, format, args); +#else auto ret_val = printf_core::vasprintf_internal(buffer, format, args); +#endif if (!ret_val.has_value()) { libc_errno = printf_core::internal_error_to_errno(ret_val.error()); return -1; diff --git a/libc/src/stdio/asprintf.h b/libc/src/stdio/asprintf.h index 168721c4f98b..26a1f1687aed 100644 --- a/libc/src/stdio/asprintf.h +++ b/libc/src/stdio/asprintf.h @@ -14,6 +14,7 @@ namespace LIBC_NAMESPACE_DECL { int asprintf(char **__restrict s, const char *__restrict format, ...); +int __asprintf_modular(char **__restrict s, const char *__restrict format, ...); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/asprintf_modular.cpp b/libc/src/stdio/asprintf_modular.cpp new file mode 100644 index 000000000000..74b62cc0c250 --- /dev/null +++ b/libc/src/stdio/asprintf_modular.cpp @@ -0,0 +1,43 @@ +//===-- Implementation of asprintf_modular ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/limits.h" +#include "src/__support/arg_list.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/stdio/asprintf.h" +#include "src/stdio/printf_core/core_structs.h" +#include "src/stdio/printf_core/error_mapper.h" +#include "src/stdio/printf_core/vasprintf_internal.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, __asprintf_modular, + (char **__restrict buffer, const char *__restrict format, + ...)) { + va_list vlist; + va_start(vlist, format); + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + va_end(vlist); + auto ret_val = printf_core::vasprintf_internal(buffer, format, args); + if (!ret_val.has_value()) { + libc_errno = printf_core::internal_error_to_errno(ret_val.error()); + return -1; + } + if (ret_val.value() > static_cast(cpp::numeric_limits::max())) { + libc_errno = + printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR); + return -1; + } + + return static_cast(ret_val.value()); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt index 51fc9d8e34fa..526400380941 100644 --- a/libc/src/stdio/baremetal/CMakeLists.txt +++ b/libc/src/stdio/baremetal/CMakeLists.txt @@ -211,10 +211,14 @@ add_entrypoint_object( libc.include.stdio ) +set(printf_srcs printf.cpp) +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND printf_srcs printf_modular.cpp) +endif() add_entrypoint_object( printf SRCS - printf.cpp + ${printf_srcs} HDRS ../printf.h DEPENDS @@ -345,10 +349,14 @@ add_entrypoint_object( libc.src.__support.arg_list ) +set(vprintf_srcs vprintf.cpp) +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND vprintf_srcs vprintf_modular.cpp) +endif() add_entrypoint_object( vprintf SRCS - vprintf.cpp + ${vprintf_srcs} HDRS ../vprintf.h DEPENDS diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp index 687cec9652c8..9728d273c67e 100644 --- a/libc/src/stdio/baremetal/printf.cpp +++ b/libc/src/stdio/baremetal/printf.cpp @@ -26,7 +26,12 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) { // destruction automatically. va_end(vlist); +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + return vfprintf_internal(stdout, format, args); +#else return vfprintf_internal(stdout, format, args); +#endif } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/printf_modular.cpp b/libc/src/stdio/baremetal/printf_modular.cpp new file mode 100644 index 000000000000..cb4b9ddcbcdb --- /dev/null +++ b/libc/src/stdio/baremetal/printf_modular.cpp @@ -0,0 +1,33 @@ +//===-- Implementation of printf_modular for baremetal ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/printf.h" + +#include "hdr/stdio_macros.h" +#include "src/__support/arg_list.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/stdio/baremetal/vfprintf_internal.h" + +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, __printf_modular, + (const char *__restrict format, ...)) { + va_list vlist; + va_start(vlist, format); + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + va_end(vlist); + + return vfprintf_internal(stdout, format, args); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/vfprintf.cpp b/libc/src/stdio/baremetal/vfprintf.cpp index 2393ca4bcdb3..c068a1018191 100644 --- a/libc/src/stdio/baremetal/vfprintf.cpp +++ b/libc/src/stdio/baremetal/vfprintf.cpp @@ -25,7 +25,12 @@ LLVM_LIBC_FUNCTION(int, vfprintf, // and pointer semantics, as well as handling // destruction automatically. +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + return vfprintf_internal(stream, format, args); +#else return vfprintf_internal(stream, format, args); +#endif } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/vfprintf_internal.h b/libc/src/stdio/baremetal/vfprintf_internal.h index d38a56481bc3..c4cd8b8ad8a8 100644 --- a/libc/src/stdio/baremetal/vfprintf_internal.h +++ b/libc/src/stdio/baremetal/vfprintf_internal.h @@ -38,6 +38,7 @@ LIBC_INLINE int write_hook(cpp::string_view str_view, void *cookie) { } // namespace internal +template LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream, const char *__restrict format, internal::ArgList &args) { @@ -48,7 +49,12 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream, stream); printf_core::Writer writer(wb); - auto retval = printf_core::printf_main(&writer, format, args); + auto retval = [&] { + if constexpr (use_modular) + return printf_core::printf_main_modular(&writer, format, args); + else + return printf_core::printf_main(&writer, format, args); + }(); if (!retval.has_value()) { libc_errno = printf_core::internal_error_to_errno(retval.error()); return -1; diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp index 0ee5bac54252..f72f5c7713fd 100644 --- a/libc/src/stdio/baremetal/vprintf.cpp +++ b/libc/src/stdio/baremetal/vprintf.cpp @@ -24,7 +24,12 @@ LLVM_LIBC_FUNCTION(int, vprintf, // and pointer semantics, as well as handling // destruction automatically. +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + return vfprintf_internal(stdout, format, args); +#else return vfprintf_internal(stdout, format, args); +#endif } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/vprintf_modular.cpp b/libc/src/stdio/baremetal/vprintf_modular.cpp new file mode 100644 index 000000000000..b14e0fae585b --- /dev/null +++ b/libc/src/stdio/baremetal/vprintf_modular.cpp @@ -0,0 +1,30 @@ +//===-- Implementation of vprintf_modular -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/vprintf.h" + +#include "hdr/stdio_macros.h" +#include "src/__support/arg_list.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/stdio/baremetal/vfprintf_internal.h" + +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, __vprintf_modular, + (const char *__restrict format, va_list vlist)) { + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + + return vfprintf_internal(stdout, format, args); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt index f7afc98142e5..0641bd463429 100644 --- a/libc/src/stdio/generic/CMakeLists.txt +++ b/libc/src/stdio/generic/CMakeLists.txt @@ -416,6 +416,10 @@ if(LLVM_LIBC_FULL_BUILD) ) endif() +if (LIBC_CONF_PRINTF_MODULAR AND NOT LIBC_TARGET_OS_IS_BAREMETAL) + message(FATAL_ERROR "modular printf is only supported in baremetal stdio") +endif() + add_generic_entrypoint_object( printf SRCS diff --git a/libc/src/stdio/gpu/CMakeLists.txt b/libc/src/stdio/gpu/CMakeLists.txt index 8412153bf580..2aea66482997 100644 --- a/libc/src/stdio/gpu/CMakeLists.txt +++ b/libc/src/stdio/gpu/CMakeLists.txt @@ -1,3 +1,7 @@ +if (LIBC_CONF_PRINTF_MODULAR) + message(FATAL_ERROR "modular printf is only supported in baremetal stdio") +endif() + add_entrypoint_object( stdin SRCS diff --git a/libc/src/stdio/printf.h b/libc/src/stdio/printf.h index 9e47ad8680f9..81b7d866a6a5 100644 --- a/libc/src/stdio/printf.h +++ b/libc/src/stdio/printf.h @@ -15,6 +15,7 @@ namespace LIBC_NAMESPACE_DECL { int printf(const char *__restrict format, ...); +int __printf_modular(const char *__restrict format, ...); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt index 362ab83afdfd..697c65e6aeb2 100644 --- a/libc/src/stdio/printf_core/CMakeLists.txt +++ b/libc/src/stdio/printf_core/CMakeLists.txt @@ -28,6 +28,9 @@ endif() if(LIBC_CONF_PRINTF_RUNTIME_DISPATCH) list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_RUNTIME_DISPATCH") endif() +if(LIBC_CONF_PRINTF_MODULAR) + list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_MODULAR") +endif() if(printf_config_copts) list(PREPEND printf_config_copts "COMPILE_OPTIONS") endif() @@ -150,10 +153,12 @@ add_header_library( ${wchar_deps} ) -add_header_library( +add_object_library( printf_main HDRS printf_main.h + SRCS + float_impl.cpp DEPENDS .parser .converter diff --git a/libc/src/stdio/printf_core/converter.h b/libc/src/stdio/printf_core/converter.h index f26ed727f05f..e931d6c34db6 100644 --- a/libc/src/stdio/printf_core/converter.h +++ b/libc/src/stdio/printf_core/converter.h @@ -28,6 +28,37 @@ namespace LIBC_NAMESPACE_DECL { namespace printf_core { +#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT +LIBC_PRINTF_MODULE((template + int convert_float(Writer *writer, + const FormatSection &to_conv)), + { + switch (to_conv.conv_name) { + case 'f': + case 'F': + return convert_float_decimal(writer, to_conv); + case 'e': + case 'E': + return convert_float_dec_exp(writer, to_conv); + case 'a': + case 'A': + return convert_float_hex_exp(writer, to_conv); + case 'g': + case 'G': + return convert_float_dec_auto(writer, to_conv); + } + __builtin_unreachable(); + }) +#endif // not LIBC_COPT_PRINTF_DISABLE_FLOAT + +#ifdef LIBC_PRINTF_DEFINE_MODULES +#define HANDLE_WRITE_MODE(MODE) \ + template int convert_float( \ + Writer * writer, const FormatSection &to_conv); +#include "src/stdio/printf_core/write_modes.def" +#undef HANDLE_WRITE_MODE +#endif // LIBC_PRINTF_DEFINE_MODULES + // convert will call a conversion function to convert the FormatSection into // its string representation, and then that will write the result to the // writer. @@ -72,16 +103,13 @@ int convert(Writer *writer, const FormatSection &to_conv) { #ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT case 'f': case 'F': - return convert_float_decimal(writer, to_conv); case 'e': case 'E': - return convert_float_dec_exp(writer, to_conv); case 'a': case 'A': - return convert_float_hex_exp(writer, to_conv); case 'g': case 'G': - return convert_float_dec_auto(writer, to_conv); + return convert_float(writer, to_conv); #endif // LIBC_COPT_PRINTF_DISABLE_FLOAT #ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT case 'r': diff --git a/libc/src/stdio/printf_core/float_impl.cpp b/libc/src/stdio/printf_core/float_impl.cpp new file mode 100644 index 000000000000..92a616b955de --- /dev/null +++ b/libc/src/stdio/printf_core/float_impl.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file instantiates the functionality needed for supporting floating +/// point arguments in modular printf builds. Non-modular printf builds +/// implicitly instantiate these functions. +/// +//===----------------------------------------------------------------------===// + +#ifdef LIBC_COPT_PRINTF_MODULAR + +#define LIBC_PRINTF_DEFINE_MODULES +#include "src/stdio/printf_core/converter.h" + +// Bring this file into the link if __printf_float is referenced. +extern "C" void __printf_float() {} + +#endif // LIBC_COPT_PRINTF_MODULAR diff --git a/libc/src/stdio/printf_core/printf_config.h b/libc/src/stdio/printf_core/printf_config.h index 8a48abdd170e..5be382a34e61 100644 --- a/libc/src/stdio/printf_core/printf_config.h +++ b/libc/src/stdio/printf_core/printf_config.h @@ -48,4 +48,24 @@ // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS +#ifdef LIBC_COPT_PRINTF_MODULAR +#define LIBC_PRINTF_MODULE_DECL __attribute__((weak)) +#else +#define LIBC_PRINTF_MODULE_DECL LIBC_INLINE +#endif + +// LIBC_PRINTF_MODULE: Defines/declares a printf module. +// +// Usage: LIBC_PRINTF_MODULE((), { }) +// +// Note that the signature is parenthesized, but the body is not. + +#define LIBC_PRINTF_MODULE_UNWRAP(...) __VA_ARGS__ +#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES) +#define LIBC_PRINTF_MODULE(SIG, ...) LIBC_PRINTF_MODULE_UNWRAP SIG __VA_ARGS__ +#else +#define LIBC_PRINTF_MODULE(SIG, ...) \ + LIBC_PRINTF_MODULE_UNWRAP SIG LIBC_PRINTF_MODULE_DECL; +#endif + #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PRINTF_CONFIG_H diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h index 1c7a7237c097..874c24464ed5 100644 --- a/libc/src/stdio/printf_core/printf_main.h +++ b/libc/src/stdio/printf_core/printf_main.h @@ -23,9 +23,9 @@ namespace LIBC_NAMESPACE_DECL { namespace printf_core { template -ErrorOr printf_main(Writer *writer, - const char *__restrict str, - internal::ArgList &args) { +ErrorOr printf_main_modular(Writer *writer, + const char *__restrict str, + internal::ArgList &args) { Parser parser(str, args); int result = 0; for (FormatSection cur_section = parser.get_next_section(); @@ -42,6 +42,16 @@ ErrorOr printf_main(Writer *writer, return writer->get_chars_written(); } +template +ErrorOr printf_main(Writer *writer, + const char *__restrict str, + internal::ArgList &args) { +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); +#endif + return printf_main_modular(writer, str, args); +} + } // namespace printf_core } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h index db6b95d49aac..fe5428e6118d 100644 --- a/libc/src/stdio/printf_core/vasprintf_internal.h +++ b/libc/src/stdio/printf_core/vasprintf_internal.h @@ -41,6 +41,7 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, constexpr size_t DEFAULT_BUFFER_SIZE = 200; +template LIBC_INLINE ErrorOr vasprintf_internal(char **ret, const char *__restrict format, internal::ArgList args) { @@ -49,7 +50,12 @@ LIBC_INLINE ErrorOr vasprintf_internal(char **ret, resize_overflow_hook); printf_core::Writer writer(wb); - auto ret_val = printf_core::printf_main(&writer, format, args); + auto ret_val = [&] { + if constexpr (use_modular) + return printf_core::printf_main_modular(&writer, format, args); + else + return printf_core::printf_main(&writer, format, args); + }(); if (!ret_val.has_value()) { *ret = nullptr; return ret_val; diff --git a/libc/src/stdio/printf_core/write_modes.def b/libc/src/stdio/printf_core/write_modes.def new file mode 100644 index 000000000000..fdce4d0241f9 --- /dev/null +++ b/libc/src/stdio/printf_core/write_modes.def @@ -0,0 +1,12 @@ +//===-- printf write mode definitions ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +HANDLE_WRITE_MODE(FILL_BUFF_AND_DROP_OVERFLOW) +HANDLE_WRITE_MODE(FLUSH_TO_STREAM) +HANDLE_WRITE_MODE(RESIZE_AND_FILL_BUFF) +HANDLE_WRITE_MODE(RUNTIME_DISPATCH) diff --git a/libc/src/stdio/printf_core/writer.h b/libc/src/stdio/printf_core/writer.h index cb45b105597d..b82792e4d5e0 100644 --- a/libc/src/stdio/printf_core/writer.h +++ b/libc/src/stdio/printf_core/writer.h @@ -21,12 +21,11 @@ namespace LIBC_NAMESPACE_DECL { namespace printf_core { +#define HANDLE_WRITE_MODE(MODE) MODE, enum class WriteMode { - FILL_BUFF_AND_DROP_OVERFLOW, - FLUSH_TO_STREAM, - RESIZE_AND_FILL_BUFF, - RUNTIME_DISPATCH, +#include "src/stdio/printf_core/write_modes.def" }; +#undef HANDLE_WRITE_MODE // Helper to omit the template argument if we are using runtime dispatch and // avoid multiple copies of the converter functions. diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp index 8364e8d59b27..d8fb1b1ad357 100644 --- a/libc/src/stdio/snprintf.cpp +++ b/libc/src/stdio/snprintf.cpp @@ -34,7 +34,12 @@ LLVM_LIBC_FUNCTION(int, snprintf, printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); printf_core::Writer writer(wb); +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + auto ret_val = printf_core::printf_main_modular(&writer, format, args); +#else auto ret_val = printf_core::printf_main(&writer, format, args); +#endif if (!ret_val.has_value()) { libc_errno = printf_core::internal_error_to_errno(ret_val.error()); return -1; diff --git a/libc/src/stdio/snprintf.h b/libc/src/stdio/snprintf.h index 92a652970407..9d350d271280 100644 --- a/libc/src/stdio/snprintf.h +++ b/libc/src/stdio/snprintf.h @@ -16,6 +16,8 @@ namespace LIBC_NAMESPACE_DECL { int snprintf(char *__restrict buffer, size_t buffsz, const char *__restrict format, ...); +int __snprintf_modular(char *__restrict buffer, size_t buffsz, + const char *__restrict format, ...); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/snprintf_modular.cpp b/libc/src/stdio/snprintf_modular.cpp new file mode 100644 index 000000000000..2e7683d56de7 --- /dev/null +++ b/libc/src/stdio/snprintf_modular.cpp @@ -0,0 +1,54 @@ +//===-- Implementation of snprintf_modular ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/snprintf.h" + +#include "src/__support/CPP/limits.h" +#include "src/__support/arg_list.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/stdio/printf_core/core_structs.h" +#include "src/stdio/printf_core/error_mapper.h" +#include "src/stdio/printf_core/printf_main.h" +#include "src/stdio/printf_core/writer.h" + +#include +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, __snprintf_modular, + (char *__restrict buffer, size_t buffsz, + const char *__restrict format, ...)) { + va_list vlist; + va_start(vlist, format); + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + va_end(vlist); + printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); + printf_core::Writer writer(wb); + + auto ret_val = printf_core::printf_main_modular(&writer, format, args); + if (!ret_val.has_value()) { + libc_errno = printf_core::internal_error_to_errno(ret_val.error()); + return -1; + } + if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer. + wb.buff[wb.buff_cur] = '\0'; + + if (ret_val.value() > static_cast(cpp::numeric_limits::max())) { + libc_errno = + printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR); + return -1; + } + + return static_cast(ret_val.value()); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp index d340096bb6d2..bbb3f64ea7b1 100644 --- a/libc/src/stdio/sprintf.cpp +++ b/libc/src/stdio/sprintf.cpp @@ -35,7 +35,12 @@ LLVM_LIBC_FUNCTION(int, sprintf, cpp::numeric_limits::max()); printf_core::Writer writer(wb); +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + auto ret_val = printf_core::printf_main_modular(&writer, format, args); +#else auto ret_val = printf_core::printf_main(&writer, format, args); +#endif if (!ret_val.has_value()) { libc_errno = printf_core::internal_error_to_errno(ret_val.error()); return -1; diff --git a/libc/src/stdio/sprintf.h b/libc/src/stdio/sprintf.h index ef65de399dc6..4008858aaaa6 100644 --- a/libc/src/stdio/sprintf.h +++ b/libc/src/stdio/sprintf.h @@ -14,6 +14,8 @@ namespace LIBC_NAMESPACE_DECL { int sprintf(char *__restrict buffer, const char *__restrict format, ...); +int __sprintf_modular(char *__restrict buffer, const char *__restrict format, + ...); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/sprintf_modular.cpp b/libc/src/stdio/sprintf_modular.cpp new file mode 100644 index 000000000000..cfed3fe4a4f4 --- /dev/null +++ b/libc/src/stdio/sprintf_modular.cpp @@ -0,0 +1,54 @@ +//===-- Implementation of sprintf_modular -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/sprintf.h" + +#include "src/__support/CPP/limits.h" +#include "src/__support/arg_list.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/stdio/printf_core/core_structs.h" +#include "src/stdio/printf_core/error_mapper.h" +#include "src/stdio/printf_core/printf_main.h" +#include "src/stdio/printf_core/writer.h" + +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, __sprintf_modular, + (char *__restrict buffer, const char *__restrict format, + ...)) { + va_list vlist; + va_start(vlist, format); + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + va_end(vlist); + + printf_core::DropOverflowBuffer wb(buffer, + cpp::numeric_limits::max()); + printf_core::Writer writer(wb); + + auto ret_val = printf_core::printf_main_modular(&writer, format, args); + if (!ret_val.has_value()) { + libc_errno = printf_core::internal_error_to_errno(ret_val.error()); + return -1; + } + wb.buff[wb.buff_cur] = '\0'; + + if (ret_val.value() > static_cast(cpp::numeric_limits::max())) { + libc_errno = + printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR); + return -1; + } + + return static_cast(ret_val.value()); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp index bd77cd886431..64d959c6edca 100644 --- a/libc/src/stdio/vasprintf.cpp +++ b/libc/src/stdio/vasprintf.cpp @@ -22,7 +22,12 @@ LLVM_LIBC_FUNCTION(int, vasprintf, internal::ArgList args(vlist); // This holder class allows for easier copying // and pointer semantics, as well as handling // destruction automatically. +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + auto ret_val = printf_core::vasprintf_internal(ret, format, args); +#else auto ret_val = printf_core::vasprintf_internal(ret, format, args); +#endif if (!ret_val.has_value()) { libc_errno = printf_core::internal_error_to_errno(ret_val.error()); return -1; diff --git a/libc/src/stdio/vasprintf.h b/libc/src/stdio/vasprintf.h index 7a98568edbc0..9f6ad87deac8 100644 --- a/libc/src/stdio/vasprintf.h +++ b/libc/src/stdio/vasprintf.h @@ -16,6 +16,8 @@ namespace LIBC_NAMESPACE_DECL { int vasprintf(char **__restrict s, const char *__restrict format, va_list vlist); +int __vasprintf_modular(char **__restrict s, const char *__restrict format, + va_list vlist); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/vasprintf_modular.cpp b/libc/src/stdio/vasprintf_modular.cpp new file mode 100644 index 000000000000..98620bb8a440 --- /dev/null +++ b/libc/src/stdio/vasprintf_modular.cpp @@ -0,0 +1,38 @@ +//===-- Implementation of vasprintf_modular ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/limits.h" +#include "src/__support/arg_list.h" +#include "src/__support/libc_errno.h" +#include "src/stdio/printf_core/core_structs.h" +#include "src/stdio/printf_core/error_mapper.h" +#include "src/stdio/printf_core/vasprintf_internal.h" +#include "src/stdio/vasprintf.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, __vasprintf_modular, + (char **__restrict ret, const char *__restrict format, + va_list vlist)) { + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + auto ret_val = printf_core::vasprintf_internal(ret, format, args); + if (!ret_val.has_value()) { + libc_errno = printf_core::internal_error_to_errno(ret_val.error()); + return -1; + } + if (ret_val.value() > static_cast(cpp::numeric_limits::max())) { + libc_errno = + printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR); + return -1; + } + return static_cast(ret_val.value()); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/vprintf.h b/libc/src/stdio/vprintf.h index 63f0f3ae62d6..70cf03f69a76 100644 --- a/libc/src/stdio/vprintf.h +++ b/libc/src/stdio/vprintf.h @@ -16,6 +16,7 @@ namespace LIBC_NAMESPACE_DECL { int vprintf(const char *__restrict format, va_list vlist); +int __vprintf_modular(const char *__restrict format, va_list vlist); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp index b65343dfefc7..bea980d6b20e 100644 --- a/libc/src/stdio/vsnprintf.cpp +++ b/libc/src/stdio/vsnprintf.cpp @@ -31,7 +31,12 @@ LLVM_LIBC_FUNCTION(int, vsnprintf, printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); printf_core::Writer writer(wb); +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + auto ret_val = printf_core::printf_main_modular(&writer, format, args); +#else auto ret_val = printf_core::printf_main(&writer, format, args); +#endif if (!ret_val.has_value()) { libc_errno = printf_core::internal_error_to_errno(ret_val.error()); return -1; diff --git a/libc/src/stdio/vsnprintf.h b/libc/src/stdio/vsnprintf.h index 27ae763f7746..0b8af0ac6ff3 100644 --- a/libc/src/stdio/vsnprintf.h +++ b/libc/src/stdio/vsnprintf.h @@ -17,6 +17,8 @@ namespace LIBC_NAMESPACE_DECL { int vsnprintf(char *__restrict buffer, size_t buffsz, const char *__restrict format, va_list vlist); +int __vsnprintf_modular(char *__restrict buffer, size_t buffsz, + const char *__restrict format, va_list vlist); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/vsnprintf_modular.cpp b/libc/src/stdio/vsnprintf_modular.cpp new file mode 100644 index 000000000000..722531160128 --- /dev/null +++ b/libc/src/stdio/vsnprintf_modular.cpp @@ -0,0 +1,51 @@ +//===-- Implementation of vsnprintf_modular ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/vsnprintf.h" + +#include "src/__support/CPP/limits.h" +#include "src/__support/arg_list.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/stdio/printf_core/core_structs.h" +#include "src/stdio/printf_core/error_mapper.h" +#include "src/stdio/printf_core/printf_main.h" +#include "src/stdio/printf_core/writer.h" + +#include +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, __vsnprintf_modular, + (char *__restrict buffer, size_t buffsz, + const char *__restrict format, va_list vlist)) { + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); + printf_core::Writer writer(wb); + + auto ret_val = printf_core::printf_main_modular(&writer, format, args); + if (!ret_val.has_value()) { + libc_errno = printf_core::internal_error_to_errno(ret_val.error()); + return -1; + } + if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer. + wb.buff[wb.buff_cur] = '\0'; + + if (ret_val.value() > static_cast(cpp::numeric_limits::max())) { + libc_errno = + printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR); + return -1; + } + + return static_cast(ret_val.value()); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp index 8affb88d2b80..3dae05fbbaad 100644 --- a/libc/src/stdio/vsprintf.cpp +++ b/libc/src/stdio/vsprintf.cpp @@ -32,7 +32,12 @@ LLVM_LIBC_FUNCTION(int, vsprintf, cpp::numeric_limits::max()); printf_core::Writer writer(wb); +#ifdef LIBC_COPT_PRINTF_MODULAR + LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float"); + auto ret_val = printf_core::printf_main_modular(&writer, format, args); +#else auto ret_val = printf_core::printf_main(&writer, format, args); +#endif if (!ret_val.has_value()) { libc_errno = printf_core::internal_error_to_errno(ret_val.error()); return -1; diff --git a/libc/src/stdio/vsprintf.h b/libc/src/stdio/vsprintf.h index abb89ba76eae..54eb6120098a 100644 --- a/libc/src/stdio/vsprintf.h +++ b/libc/src/stdio/vsprintf.h @@ -16,6 +16,8 @@ namespace LIBC_NAMESPACE_DECL { int vsprintf(char *__restrict buffer, const char *__restrict format, va_list vlist); +int __vsprintf_modular(char *__restrict buffer, const char *__restrict format, + va_list vlist); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/vsprintf_modular.cpp b/libc/src/stdio/vsprintf_modular.cpp new file mode 100644 index 000000000000..844d1e3cdd55 --- /dev/null +++ b/libc/src/stdio/vsprintf_modular.cpp @@ -0,0 +1,50 @@ +//===-- Implementation of vsprintf_modular ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/vsprintf.h" + +#include "src/__support/CPP/limits.h" +#include "src/__support/arg_list.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/stdio/printf_core/core_structs.h" +#include "src/stdio/printf_core/error_mapper.h" +#include "src/stdio/printf_core/printf_main.h" +#include "src/stdio/printf_core/writer.h" + +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, __vsprintf_modular, + (char *__restrict buffer, const char *__restrict format, + va_list vlist)) { + internal::ArgList args(vlist); // This holder class allows for easier copying + // and pointer semantics, as well as handling + // destruction automatically. + + printf_core::DropOverflowBuffer wb(buffer, + cpp::numeric_limits::max()); + printf_core::Writer writer(wb); + + auto ret_val = printf_core::printf_main_modular(&writer, format, args); + if (!ret_val.has_value()) { + libc_errno = printf_core::internal_error_to_errno(ret_val.error()); + return -1; + } + wb.buff[wb.buff_cur] = '\0'; + + if (ret_val.value() > static_cast(cpp::numeric_limits::max())) { + libc_errno = + printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR); + return -1; + } + return static_cast(ret_val.value()); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/utils/hdrgen/hdrgen/header.py b/libc/utils/hdrgen/hdrgen/header.py index b69d22494a7a..1af741844bd0 100644 --- a/libc/utils/hdrgen/hdrgen/header.py +++ b/libc/utils/hdrgen/hdrgen/header.py @@ -177,7 +177,7 @@ class HeaderFile: for typ in self.all_types() } | { - PurePosixPath("llvm-libc-macros") / f"{attr}.h" + PurePosixPath("llvm-libc-macros") / f"{attr.split('(')[0]}.h" for attr in self.all_attributes() - COMMON_ATTRIBUTES } ) diff --git a/libc/utils/hdrgen/tests/expected_output/test_header.h b/libc/utils/hdrgen/tests/expected_output/test_header.h index 49112a353f7b..7704176bbc3d 100644 --- a/libc/utils/hdrgen/tests/expected_output/test_header.h +++ b/libc/utils/hdrgen/tests/expected_output/test_header.h @@ -13,6 +13,7 @@ #include "llvm-libc-macros/float16-macros.h" #include "llvm-libc-macros/CONST_FUNC_A.h" +#include "llvm-libc-macros/MACRO_ATTR.h" #include "llvm-libc-macros/test_more-macros.h" #include "llvm-libc-macros/test_small-macros.h" #include "llvm-libc-types/float128.h" @@ -32,7 +33,7 @@ enum { __BEGIN_C_DECLS -CONST_FUNC_A void func_a(void) __NOEXCEPT; +CONST_FUNC_A MACRO_ATTR(A) void func_a(void) __NOEXCEPT; #ifdef LIBC_TYPES_HAS_FLOAT128 float128 func_b(void) __NOEXCEPT; diff --git a/libc/utils/hdrgen/tests/expected_output/test_small.json b/libc/utils/hdrgen/tests/expected_output/test_small.json index 8502df23b9a4..e62fce1043ee 100644 --- a/libc/utils/hdrgen/tests/expected_output/test_small.json +++ b/libc/utils/hdrgen/tests/expected_output/test_small.json @@ -5,6 +5,7 @@ "includes": [ "__llvm-libc-common.h", "llvm-libc-macros/CONST_FUNC_A.h", + "llvm-libc-macros/MACRO_ATTR.h", "llvm-libc-macros/test_more-macros.h", "llvm-libc-macros/test_small-macros.h", "llvm-libc-types/float128.h", diff --git a/libc/utils/hdrgen/tests/expected_output/test_small_proxy.h b/libc/utils/hdrgen/tests/expected_output/test_small_proxy.h index bf98581ef476..1dd209472e33 100644 --- a/libc/utils/hdrgen/tests/expected_output/test_small_proxy.h +++ b/libc/utils/hdrgen/tests/expected_output/test_small_proxy.h @@ -12,6 +12,7 @@ #ifdef LIBC_FULL_BUILD #include "llvm-libc-macros/CONST_FUNC_A.h" +#include "llvm-libc-macros/MACRO_ATTR.h" #include "llvm-libc-macros/test_more-macros.h" #include "llvm-libc-macros/test_small-macros.h" #include "llvm-libc-types/float128.h" diff --git a/libc/utils/hdrgen/tests/input/merge1.yaml b/libc/utils/hdrgen/tests/input/merge1.yaml index 950abd177032..9abcf7f26671 100644 --- a/libc/utils/hdrgen/tests/input/merge1.yaml +++ b/libc/utils/hdrgen/tests/input/merge1.yaml @@ -17,3 +17,4 @@ functions: - stdc attributes: - CONST_FUNC_A + - MACRO_ATTR(A)