-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[libc] Modular printf option (float only) #147426
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: users/mysterymath/modular-printf/clang
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
//===-- Implementation of printf_modular-----------------------------------===// | ||
// | ||
// 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 "src/__support/File/file.h" | ||
#include "src/__support/arg_list.h" | ||
#include "src/__support/macros/config.h" | ||
#include "src/stdio/printf_core/vfprintf_internal.h" | ||
|
||
#include "hdr/types/FILE.h" | ||
#include <stdarg.h> | ||
|
||
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE | ||
#define PRINTF_STDOUT LIBC_NAMESPACE::stdout | ||
#else // LIBC_COPT_STDIO_USE_SYSTEM_FILE | ||
#define PRINTF_STDOUT ::stdout | ||
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE | ||
|
||
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); | ||
int ret_val = printf_core::vfprintf_internal_modular( | ||
reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args); | ||
return ret_val; | ||
} | ||
|
||
} // namespace LIBC_NAMESPACE_DECL |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1122,11 +1122,23 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer, | |
} | ||
} | ||
|
||
template <WriteMode write_mode> | ||
LIBC_PRINTF_MODULAR_DECL int | ||
convert_float_decimal(Writer<write_mode> *writer, const FormatSection &to_conv); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be a separate PR, but can we change all the |
||
template <WriteMode write_mode> | ||
LIBC_PRINTF_MODULAR_DECL int | ||
convert_float_dec_exp(Writer<write_mode> *writer, const FormatSection &to_conv); | ||
template <WriteMode write_mode> | ||
LIBC_PRINTF_MODULAR_DECL int | ||
convert_float_dec_auto(Writer<write_mode> *writer, | ||
const FormatSection &to_conv); | ||
|
||
#ifdef LIBC_PRINTF_DEFINE_MODULAR | ||
// TODO: unify the float converters to remove the duplicated checks for inf/nan. | ||
|
||
template <WriteMode write_mode> | ||
LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer, | ||
const FormatSection &to_conv) { | ||
int convert_float_decimal(Writer<write_mode> *writer, | ||
const FormatSection &to_conv) { | ||
if (to_conv.length_modifier == LengthModifier::L) { | ||
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw; | ||
fputil::FPBits<long double> float_bits(float_raw); | ||
|
@@ -1147,8 +1159,8 @@ LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer, | |
} | ||
|
||
template <WriteMode write_mode> | ||
LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer, | ||
const FormatSection &to_conv) { | ||
int convert_float_dec_exp(Writer<write_mode> *writer, | ||
const FormatSection &to_conv) { | ||
if (to_conv.length_modifier == LengthModifier::L) { | ||
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw; | ||
fputil::FPBits<long double> float_bits(float_raw); | ||
|
@@ -1169,8 +1181,8 @@ LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer, | |
} | ||
|
||
template <WriteMode write_mode> | ||
LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer, | ||
const FormatSection &to_conv) { | ||
int convert_float_dec_auto(Writer<write_mode> *writer, | ||
const FormatSection &to_conv) { | ||
if (to_conv.length_modifier == LengthModifier::L) { | ||
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw; | ||
fputil::FPBits<long double> float_bits(float_raw); | ||
|
@@ -1189,6 +1201,7 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer, | |
|
||
return convert_inf_nan(writer, to_conv); | ||
} | ||
#endif | ||
|
||
} // namespace printf_core | ||
} // namespace LIBC_NAMESPACE_DECL | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#ifdef LIBC_COPT_PRINTF_MODULAR | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file needs the copyright header. |
||
#include "src/__support/arg_list.h" | ||
|
||
#define LIBC_PRINTF_DEFINE_MODULAR | ||
#include "src/stdio/printf_core/float_dec_converter.h" | ||
#include "src/stdio/printf_core/float_hex_converter.h" | ||
#include "src/stdio/printf_core/parser.h" | ||
Comment on lines
+4
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. given this, do you need to modify |
||
|
||
namespace LIBC_NAMESPACE_DECL { | ||
namespace printf_core { | ||
template class Parser<internal::ArgList>; | ||
template class Parser<internal::DummyArgList<false>>; | ||
template class Parser<internal::DummyArgList<true>>; | ||
template class Parser<internal::StructArgList<false>>; | ||
template class Parser<internal::StructArgList<true>>; | ||
|
||
#define INSTANTIATE_CONVERT_FN(NAME) \ | ||
template int NAME<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>( \ | ||
Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> * writer, \ | ||
const FormatSection &to_conv); \ | ||
template int NAME<WriteMode::FLUSH_TO_STREAM>( \ | ||
Writer<WriteMode::FLUSH_TO_STREAM> * writer, \ | ||
const FormatSection &to_conv); \ | ||
template int NAME<WriteMode::RESIZE_AND_FILL_BUFF>( \ | ||
Writer<WriteMode::RESIZE_AND_FILL_BUFF> * writer, \ | ||
const FormatSection &to_conv); \ | ||
template int NAME<WriteMode::RUNTIME_DISPATCH>( \ | ||
Writer<WriteMode::RUNTIME_DISPATCH> * writer, \ | ||
const FormatSection &to_conv) | ||
|
||
INSTANTIATE_CONVERT_FN(convert_float_decimal); | ||
INSTANTIATE_CONVERT_FN(convert_float_dec_exp); | ||
INSTANTIATE_CONVERT_FN(convert_float_dec_auto); | ||
INSTANTIATE_CONVERT_FN(convert_float_hex_exp); | ||
|
||
} // namespace printf_core | ||
} // namespace LIBC_NAMESPACE_DECL | ||
|
||
// Bring this file into the link if __printf_float is referenced. | ||
extern "C" void __printf_float() {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One risk with this strategy: what happens if this file is compiled with If I wanted to be sure of this technique, I'd either enforce via compile options that everything in this file is in the same code section, or else add further There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
To my knowledge, this isn't possible. For LLD at least, the weakness of the reference isn't examined when determining whether or not a reference preserves a section. Weakness only controls whether or not an undefined or duplicate reference fails the link and whether an undefined reference extracts archive members. (There's some other differences with shared libraries unimportant to this case.) I'd be surprised if other linkers differ; wasn't this usage the whole point of allowing undefined weak references? |
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -236,11 +236,7 @@ template <typename ArgProvider> class Parser { | |
case ('A'): | ||
case ('g'): | ||
case ('G'): | ||
if (lm != LengthModifier::L) { | ||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index); | ||
} else { | ||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index); | ||
} | ||
write_float_arg_val(section, lm, conv_index); | ||
break; | ||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT | ||
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT | ||
|
@@ -299,6 +295,12 @@ template <typename ArgProvider> class Parser { | |
return section; | ||
} | ||
|
||
LIBC_PRINTF_MODULAR_DECL void write_float_arg_val(FormatSection §ion, | ||
LengthModifier lm, | ||
size_t conv_index); | ||
LIBC_PRINTF_MODULAR_DECL TypeDesc float_type_desc(LengthModifier lm); | ||
LIBC_PRINTF_MODULAR_DECL bool advance_arg_if_float(TypeDesc cur_type_desc); | ||
|
||
private: | ||
// parse_flags parses the flags inside a format string. It assumes that | ||
// str[*local_pos] is inside a format specifier, and parses any flags it | ||
|
@@ -474,10 +476,9 @@ template <typename ArgProvider> class Parser { | |
args_cur.template next_var<uint64_t>(); | ||
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT | ||
// Floating point numbers are stored separately from the other arguments. | ||
else if (cur_type_desc == type_desc_from_type<double>()) | ||
args_cur.template next_var<double>(); | ||
else if (cur_type_desc == type_desc_from_type<long double>()) | ||
args_cur.template next_var<long double>(); | ||
else if (&Parser::advance_arg_if_float && | ||
advance_arg_if_float(cur_type_desc)) | ||
; | ||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT | ||
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT | ||
// Floating point numbers may be stored separately from the other | ||
|
@@ -630,10 +631,7 @@ template <typename ArgProvider> class Parser { | |
case ('A'): | ||
case ('g'): | ||
case ('G'): | ||
if (lm != LengthModifier::L) | ||
conv_size = type_desc_from_type<double>(); | ||
else | ||
conv_size = type_desc_from_type<long double>(); | ||
conv_size = float_type_desc(lm); | ||
break; | ||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT | ||
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT | ||
|
@@ -682,6 +680,38 @@ template <typename ArgProvider> class Parser { | |
#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE | ||
}; | ||
|
||
#ifdef LIBC_PRINTF_DEFINE_MODULAR | ||
template <typename ArgParser> | ||
void Parser<ArgParser>::write_float_arg_val(FormatSection §ion, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. given that these are non-inline functions in a header, do they need to be in an anonymous namespace to avoid ODR issues? Here and elsewhere. |
||
LengthModifier lm, | ||
size_t conv_index) { | ||
if (lm != LengthModifier::L) { | ||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index); | ||
} else { | ||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index); | ||
} | ||
} | ||
|
||
template <typename ArgParser> | ||
TypeDesc Parser<ArgParser>::float_type_desc(LengthModifier lm) { | ||
if (lm != LengthModifier::L) | ||
return type_desc_from_type<double>(); | ||
else | ||
return type_desc_from_type<long double>(); | ||
} | ||
|
||
template <typename ArgParser> | ||
bool Parser<ArgParser>::advance_arg_if_float(TypeDesc cur_type_desc) { | ||
if (cur_type_desc == type_desc_from_type<double>()) | ||
args_cur.template next_var<double>(); | ||
else if (cur_type_desc == type_desc_from_type<long double>()) | ||
args_cur.template next_var<long double>(); | ||
else | ||
return false; | ||
return true; | ||
} | ||
#endif | ||
|
||
} // namespace printf_core | ||
} // namespace LIBC_NAMESPACE_DECL | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense to have
__printf_float
as a separate entrypoint so that it can be controlled per-platform with the same mechanism as other entrypoints?