Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[libc] Add fixed point support to printf (#82707)
This patch adds the r, R, k, and K conversion specifiers to printf, with accompanying tests. They are guarded behind the LIBC_COPT_PRINTF_DISABLE_FIXED_POINT flag as well as automatic fixed point support detection.
- Loading branch information
1 parent
13fd4bf
commit 8e3b605
Showing
17 changed files
with
807 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
//===-- printf_fixed_conv_fuzz.cpp ----------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
/// | ||
/// Fuzzing test for llvm-libc printf %f/e/g/a implementations. | ||
/// | ||
//===----------------------------------------------------------------------===// | ||
#include "src/stdio/snprintf.h" | ||
|
||
#include "include/llvm-libc-macros/stdfix-macros.h" | ||
#include "src/__support/fixed_point/fx_bits.h" | ||
#include "src/__support/fixed_point/fx_rep.h" | ||
|
||
#include <stddef.h> | ||
#include <stdint.h> | ||
|
||
#include "utils/MPFRWrapper/mpfr_inc.h" | ||
|
||
constexpr int MAX_SIZE = 10000; | ||
|
||
inline bool simple_streq(char *first, char *second, int length) { | ||
for (int i = 0; i < length; ++i) | ||
if (first[i] != second[i]) | ||
return false; | ||
|
||
return true; | ||
} | ||
|
||
inline int clamp(int num, int max) { | ||
if (num > max) | ||
return max; | ||
if (num < -max) | ||
return -max; | ||
return num; | ||
} | ||
|
||
enum class TestResult { | ||
Success, | ||
BufferSizeFailed, | ||
LengthsDiffer, | ||
StringsNotEqual, | ||
}; | ||
|
||
template <typename F> | ||
inline TestResult test_vals(const char *fmt, uint64_t num, int prec, | ||
int width) { | ||
typename LIBC_NAMESPACE::fixed_point::FXRep<F>::StorageType raw_num = num; | ||
|
||
auto raw_num_bits = LIBC_NAMESPACE::fixed_point::FXBits<F>(raw_num); | ||
|
||
// This needs to be a float with enough bits of precision to hold the fixed | ||
// point number. | ||
static_assert(sizeof(long double) > sizeof(long accum)); | ||
|
||
// build a long double that is equivalent to the fixed point number. | ||
long double ld_num = | ||
static_cast<long double>(raw_num_bits.get_integral()) + | ||
(static_cast<long double>(raw_num_bits.get_fraction()) / | ||
static_cast<long double>(1ll << raw_num_bits.get_exponent())); | ||
|
||
if (raw_num_bits.get_sign()) | ||
ld_num = -ld_num; | ||
|
||
// Call snprintf on a nullptr to get the buffer size. | ||
int buffer_size = LIBC_NAMESPACE::snprintf(nullptr, 0, fmt, width, prec, num); | ||
|
||
if (buffer_size < 0) | ||
return TestResult::BufferSizeFailed; | ||
|
||
char *test_buff = new char[buffer_size + 1]; | ||
char *reference_buff = new char[buffer_size + 1]; | ||
|
||
int test_result = 0; | ||
int reference_result = 0; | ||
|
||
test_result = LIBC_NAMESPACE::snprintf(test_buff, buffer_size + 1, fmt, width, | ||
prec, num); | ||
|
||
// The fixed point format is defined to be %f equivalent. | ||
reference_result = mpfr_snprintf(reference_buff, buffer_size + 1, "%*.*Lf", | ||
width, prec, ld_num); | ||
|
||
// All of these calls should return that they wrote the same amount. | ||
if (test_result != reference_result || test_result != buffer_size) | ||
return TestResult::LengthsDiffer; | ||
|
||
if (!simple_streq(test_buff, reference_buff, buffer_size)) | ||
return TestResult::StringsNotEqual; | ||
|
||
delete[] test_buff; | ||
delete[] reference_buff; | ||
return TestResult::Success; | ||
} | ||
|
||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | ||
// const uint8_t raw_data[] = {0x8d,0x43,0x40,0x0,0x0,0x0,}; | ||
// data = raw_data; | ||
// size = sizeof(raw_data); | ||
int prec = 0; | ||
int width = 0; | ||
|
||
LIBC_NAMESPACE::fixed_point::FXRep<long accum>::StorageType raw_num = 0; | ||
|
||
// Copy as many bytes of data as will fit into num, prec, and with. Any extras | ||
// are ignored. | ||
for (size_t cur = 0; cur < size; ++cur) { | ||
if (cur < sizeof(raw_num)) { | ||
raw_num = (raw_num << 8) + data[cur]; | ||
} else if (cur < sizeof(raw_num) + sizeof(prec)) { | ||
prec = (prec << 8) + data[cur]; | ||
} else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) { | ||
width = (width << 8) + data[cur]; | ||
} | ||
} | ||
|
||
width = clamp(width, MAX_SIZE); | ||
prec = clamp(prec, MAX_SIZE); | ||
|
||
TestResult result; | ||
result = test_vals<long accum>("%*.*lk", raw_num, prec, width); | ||
if (result != TestResult::Success) | ||
__builtin_trap(); | ||
|
||
result = test_vals<unsigned long accum>("%*.*lK", raw_num, prec, width); | ||
if (result != TestResult::Success) | ||
__builtin_trap(); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.