[libc-commits] [libc] [libc] Add str_to_* and rpc_* shared tests. (PR #190351)

via libc-commits libc-commits at lists.llvm.org
Fri Apr 3 12:38:23 PDT 2026


https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/190351

>From efe03aae4d45a64b8242a1252665a9ce6c7fe3a2 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 3 Apr 2026 15:33:31 +0000
Subject: [PATCH 1/4] [libc] Add str_to_* and rpc_* shared tests.

Also fix several things for LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE to make them build.
---
 libc/src/__support/FPUtil/FPBits.h            |  4 +-
 libc/src/__support/RPC/rpc_server.h           |  6 +-
 libc/src/__support/float_to_string.h          |  1 +
 .../stdio/printf_core/float_dec_converter.h   | 21 ++++++-
 .../printf_core/float_dec_converter_limited.h | 11 +++-
 .../stdio/printf_core/float_hex_converter.h   | 16 +++++-
 .../printf_core/float_inf_nan_converter.h     |  9 ++-
 libc/src/stdio/printf_core/parser.h           |  9 +++
 libc/src/stdio/scanf_core/converter_utils.h   |  2 +
 libc/test/shared/CMakeLists.txt               | 24 ++++++++
 libc/test/shared/shared_rpc_test.cpp          | 44 +++++++++++++++
 libc/test/shared/shared_str_to_num_test.cpp   | 56 +++++++++++++++++++
 12 files changed, 193 insertions(+), 10 deletions(-)
 create mode 100644 libc/test/shared/shared_rpc_test.cpp
 create mode 100644 libc/test/shared/shared_str_to_num_test.cpp

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index ce4925bae125a..7028fd4f38950 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -798,7 +798,9 @@ template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
       return FPType::IEEE754_Binary64;
     else if constexpr (LDBL_MANT_DIG == 64)
       return FPType::X86_Binary80;
-    else if constexpr (LDBL_MANT_DIG == 113)
+    // TODO: properly treat double-double type.
+    // else if constexpr (LDBL_MANT_DIG == 113)
+    else
       return FPType::IEEE754_Binary128;
   }
 #if defined(LIBC_TYPES_HAS_FLOAT16)
diff --git a/libc/src/__support/RPC/rpc_server.h b/libc/src/__support/RPC/rpc_server.h
index 3dfd79c6ecd0d..316e275f3cf6f 100644
--- a/libc/src/__support/RPC/rpc_server.h
+++ b/libc/src/__support/RPC/rpc_server.h
@@ -43,8 +43,10 @@
 #define LIBC_COPT_PRINTF_DISABLE_WIDE
 #endif
 
+// TODO: Make printf and our internal tools able to force the long double types
+// properly.
 // The 'long double' type is 8 bytes.
-#define LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64
+// #define LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64
 
 #include "shared/rpc.h"
 #include "shared/rpc_opcodes.h"
@@ -226,7 +228,7 @@ LIBC_INLINE static void handle_printf(rpc::Server::Port &port,
         writer.write(cur_section.raw_string);
       }
     }
-    buffer_size[lane] = writer.get_chars_written();
+    buffer_size[lane] = static_cast<int>(writer.get_chars_written());
   }
 
   // Receive any strings from the client and push them into a buffer.
diff --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h
index 44853059a8da3..683fb75aeda3f 100644
--- a/libc/src/__support/float_to_string.h
+++ b/libc/src/__support/float_to_string.h
@@ -606,6 +606,7 @@ class FloatToString {
 };
 
 #if !defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64) &&                             \
+    !defined(LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE) &&                       \
     !defined(LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD)
 // --------------------------- LONG DOUBLE FUNCTIONS ---------------------------
 
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index ed004f9a26a13..b15d7c6e04596 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -29,7 +29,11 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+using StorageType = UInt128;
+#else
 using StorageType = fputil::FPBits<long double>::StorageType;
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 using DecimalString = IntegerToString<intmax_t>;
 using ExponentString =
     IntegerToString<intmax_t, radix::Dec::WithWidth<2>::WithSign>;
@@ -243,7 +247,9 @@ template <WriteMode write_mode> class FloatWriter {
   // -exponent will never overflow because all long double types we support
   // have at most 15 bits of mantissa and the C standard defines an int as
   // being at least 16 bits.
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   static_assert(fputil::FPBits<long double>::EXP_LEN < (sizeof(int) * 8));
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 
 public:
   LIBC_INLINE FloatWriter(Writer<write_mode> *init_writer,
@@ -1127,6 +1133,7 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
 template <WriteMode write_mode>
 LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
                                       const FormatSection &to_conv) {
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   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);
@@ -1134,7 +1141,9 @@ LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
       return convert_float_decimal_typed<long double>(writer, to_conv,
                                                       float_bits);
     }
-  } else {
+  } else
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  {
     fputil::FPBits<double>::StorageType float_raw =
         static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
     fputil::FPBits<double> float_bits(float_raw);
@@ -1149,6 +1158,7 @@ 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) {
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   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);
@@ -1156,7 +1166,9 @@ LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
       return convert_float_dec_exp_typed<long double>(writer, to_conv,
                                                       float_bits);
     }
-  } else {
+  } else
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  {
     fputil::FPBits<double>::StorageType float_raw =
         static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
     fputil::FPBits<double> float_bits(float_raw);
@@ -1171,6 +1183,7 @@ 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) {
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   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);
@@ -1178,7 +1191,9 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
       return convert_float_dec_auto_typed<long double>(writer, to_conv,
                                                        float_bits);
     }
-  } else {
+  } else
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  {
     fputil::FPBits<double>::StorageType float_raw =
         static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
     fputil::FPBits<double> float_bits(float_raw);
diff --git a/libc/src/stdio/printf_core/float_dec_converter_limited.h b/libc/src/stdio/printf_core/float_dec_converter_limited.h
index 0f85d0a8d26b4..6c90cf4404fcb 100644
--- a/libc/src/stdio/printf_core/float_dec_converter_limited.h
+++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h
@@ -59,7 +59,11 @@ namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
 enum class ConversionType { E, F, G };
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+using StorageType = UInt128;
+#else
 using StorageType = fputil::FPBits<long double>::StorageType;
+#endif
 
 constexpr unsigned MAX_DIGITS = 39;
 constexpr size_t DF_BITS = 320;
@@ -633,14 +637,17 @@ template <WriteMode write_mode>
 LIBC_INLINE int convert_float_outer(Writer<write_mode> *writer,
                                     const FormatSection &to_conv,
                                     ConversionType ctype) {
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   if (to_conv.length_modifier == LengthModifier::L) {
-    fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
+    StorageType float_raw = to_conv.conv_val_raw;
     fputil::FPBits<long double> float_bits(float_raw);
     if (!float_bits.is_inf_or_nan()) {
       return convert_float_typed<long double>(writer, to_conv, float_bits,
                                               ctype);
     }
-  } else {
+  } else
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  {
     fputil::FPBits<double>::StorageType float_raw =
         static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
     fputil::FPBits<double> float_bits(float_raw);
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index 9b57f1d803e74..0357dbd4d3c4c 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -28,14 +28,21 @@ namespace printf_core {
 template <WriteMode write_mode>
 LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
                                       const FormatSection &to_conv) {
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  using LDBits = fputil::FPBits<double>;
+  using StorageType = LDBits::StorageType;
+#else
   using LDBits = fputil::FPBits<long double>;
   using StorageType = LDBits::StorageType;
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 
   bool is_negative;
   int exponent;
   StorageType mantissa;
   bool is_inf_or_nan;
   uint32_t fraction_bits;
+
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   if (to_conv.length_modifier == LengthModifier::L) {
     fraction_bits = LDBits::FRACTION_LEN;
     LDBits::StorageType float_raw = to_conv.conv_val_raw;
@@ -44,7 +51,9 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
     exponent = float_bits.get_explicit_exponent();
     mantissa = float_bits.get_explicit_mantissa();
     is_inf_or_nan = float_bits.is_inf_or_nan();
-  } else {
+  } else
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  {
     using LBits = fputil::FPBits<double>;
     fraction_bits = LBits::FRACTION_LEN;
     LBits::StorageType float_raw =
@@ -83,8 +92,13 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
   // Since the number is in bits, we divide by 4, and then add one to account
   // for the extra implicit bit. We use the larger of the two possible values
   // since the size must be constant.
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  constexpr size_t MANT_BUFF_LEN =
+      (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
+#else
   constexpr size_t MANT_BUFF_LEN =
       (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   char mant_buffer[MANT_BUFF_LEN];
 
   size_t mant_len = (fraction_bits / BITS_IN_HEX_DIGIT) + 1;
diff --git a/libc/src/stdio/printf_core/float_inf_nan_converter.h b/libc/src/stdio/printf_core/float_inf_nan_converter.h
index ce31d7ae55499..e3333978f2194 100644
--- a/libc/src/stdio/printf_core/float_inf_nan_converter.h
+++ b/libc/src/stdio/printf_core/float_inf_nan_converter.h
@@ -22,7 +22,11 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+using StorageType = UInt128;
+#else
 using StorageType = fputil::FPBits<long double>::StorageType;
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 
 template <WriteMode write_mode>
 LIBC_INLINE int convert_inf_nan(Writer<write_mode> *writer,
@@ -31,12 +35,15 @@ LIBC_INLINE int convert_inf_nan(Writer<write_mode> *writer,
   // the appropriate case based on the case of the conversion.
   bool is_negative;
   StorageType mantissa;
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   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);
     is_negative = float_bits.is_neg();
     mantissa = float_bits.get_mantissa();
-  } else {
+  } else
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  {
     fputil::FPBits<double>::StorageType float_raw =
         static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
     fputil::FPBits<double> float_bits(float_raw);
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index 7f9dd7ae627a4..ed24250491517 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -15,6 +15,7 @@
 #include "src/__support/CPP/optional.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
 #include "src/__support/str_to_integer.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_config.h"
@@ -40,9 +41,11 @@ template <typename T> struct int_type_of {
 template <> struct int_type_of<double> {
   using type = fputil::FPBits<double>::StorageType;
 };
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 template <> struct int_type_of<long double> {
   using type = fputil::FPBits<long double>::StorageType;
 };
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 
 #ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
 template <typename T>
@@ -254,7 +257,9 @@ template <typename ArgProvider> class Parser {
         if (lm != LengthModifier::L) {
           WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);
         } else {
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
           WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index);
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
         }
         break;
 #endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
@@ -493,8 +498,10 @@ template <typename ArgProvider> class Parser {
       // 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>();
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
       else if (cur_type_desc == type_desc_from_type<long double>())
         args_cur.template next_var<long double>();
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 #endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
 #ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
       // Floating point numbers may be stored separately from the other
@@ -660,8 +667,10 @@ template <typename ArgProvider> class Parser {
         case ('G'):
           if (lm != LengthModifier::L)
             conv_size = type_desc_from_type<double>();
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
           else
             conv_size = type_desc_from_type<long double>();
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
           break;
 #endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
 #ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
diff --git a/libc/src/stdio/scanf_core/converter_utils.h b/libc/src/stdio/scanf_core/converter_utils.h
index 6f4d16cffb19c..979d180b01a64 100644
--- a/libc/src/stdio/scanf_core/converter_utils.h
+++ b/libc/src/stdio/scanf_core/converter_utils.h
@@ -83,11 +83,13 @@ LIBC_INLINE void write_float_with_length(char *str,
     *reinterpret_cast<double *>(output_ptr) = value;
     break;
   }
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   case (LengthModifier::L): {
     auto value = internal::strtofloatingpoint<long double>(str);
     *reinterpret_cast<long double *>(output_ptr) = value;
     break;
   }
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   default: {
     auto value = internal::strtofloatingpoint<float>(str);
     *reinterpret_cast<float *>(output_ptr) = value;
diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt
index 8710b0513d23b..87bbd6bf448c5 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -276,3 +276,27 @@ add_fp_unittest(
     libc.src.__support.math.ceill
     libc.src.__support.math.log
 )
+
+add_fp_unittest(
+  shared_str_to_num_test
+  SUITE
+    libc-shared-tests
+  SRCS
+    shared_str_to_num_test.cpp
+  DEPENDS
+    libc.src.__support.str_to_float
+    libc.src.__support.str_to_integer
+)
+
+add_fp_unittest(
+  shared_rpc_test
+  SUITE
+    libc-shared-tests
+  SRCS
+    shared_rpc_test.cpp
+  DEPENDS
+    libc.src.stdio.printf_core.converter
+    libc.src.stdio.printf_core.parser
+    libc.src.stdio.printf_core.writer
+    libc.src.__support.arg_list
+)
diff --git a/libc/test/shared/shared_rpc_test.cpp b/libc/test/shared/shared_rpc_test.cpp
new file mode 100644
index 0000000000000..0697d938a4db9
--- /dev/null
+++ b/libc/test/shared/shared_rpc_test.cpp
@@ -0,0 +1,44 @@
+//===-- Unittests for shared RPC server -----------------------------------===//
+//
+// 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 "shared/rpc.h"
+#include "shared/rpc_server.h"
+#include "test/UnitTest/Test.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+TEST(LlvmLibcSharedRpcTest, TestIncrement) {
+  constexpr uint32_t port_count = 1;
+  constexpr uint32_t lane_size = 1;
+  alignas(64)
+      uint8_t buffer[::rpc::Server::allocation_size(lane_size, port_count)];
+
+  ::rpc::Server server(port_count, buffer);
+  ::rpc::Client client(port_count, buffer);
+
+  // Client side: open a port and send data
+  auto client_port = client.open<LIBC_TEST_INCREMENT>();
+  uint64_t data = 42;
+  client_port.send(
+      [&](::rpc::Buffer *buffer, uint32_t) { buffer->data[0] = data; });
+
+  // Server side: handle the port
+  auto server_port = server.try_open(lane_size);
+  ASSERT_TRUE(server_port.has_value());
+  ::rpc::RPCStatus status =
+      shared::handle_libc_opcodes(*server_port, lane_size);
+  EXPECT_EQ(status, ::rpc::RPC_SUCCESS);
+
+  // Client side: receive the response
+  client_port.recv(
+      [&](::rpc::Buffer *buffer, uint32_t) { data = buffer->data[0]; });
+
+  EXPECT_EQ(data, 43UL);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/shared/shared_str_to_num_test.cpp b/libc/test/shared/shared_str_to_num_test.cpp
new file mode 100644
index 0000000000000..e65db141c9bb0
--- /dev/null
+++ b/libc/test/shared/shared_str_to_num_test.cpp
@@ -0,0 +1,56 @@
+//===-- Unittests for shared string to number functions -------------------===//
+//
+// 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 "shared/str_to_float.h"
+#include "shared/str_to_integer.h"
+#include "test/UnitTest/Test.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+TEST(LlvmLibcSharedStrToNumTest, IntegerTests) {
+  {
+    auto result = shared::strtointeger<int>("123", 10);
+    EXPECT_EQ(result.value, 123);
+    EXPECT_EQ(result.parsed_len, ptrdiff_t(3));
+    EXPECT_EQ(result.error, 0);
+  }
+  {
+    auto result = shared::strtointeger<int>("  -0x123", 0);
+    EXPECT_EQ(result.value, -0x123);
+    EXPECT_EQ(result.parsed_len, ptrdiff_t(8));
+    EXPECT_EQ(result.error, 0);
+  }
+}
+
+TEST(LlvmLibcSharedStrToNumTest, FloatTests) {
+  {
+    // 1.25 = 1.01b = 5 * 2^-2
+    shared::ExpandedFloat<double> input;
+    input.mantissa = 5;
+    input.exponent = -2;
+    auto result = shared::binary_exp_to_float<double>(
+        input, false, shared::RoundDirection::Nearest);
+    EXPECT_EQ(result.num.mantissa, 0x4000000000000UL);
+    EXPECT_EQ(result.num.exponent, 1023);
+    EXPECT_EQ(result.error, 0);
+  }
+  {
+    // 1.25 = 125 * 10^-2
+    shared::ExpandedFloat<double> input;
+    input.mantissa = 125;
+    input.exponent = -2;
+    const char *str = "1.25";
+    auto result = shared::decimal_exp_to_float<double>(
+        input, false, shared::RoundDirection::Nearest, str);
+    EXPECT_EQ(result.num.mantissa, 0x14000000000000UL);
+    EXPECT_EQ(result.num.exponent, 1023);
+    EXPECT_EQ(result.error, 0);
+  }
+}
+
+} // namespace LIBC_NAMESPACE_DECL

>From e59249a128cb93e04208526c6398a74517925ad9 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 3 Apr 2026 18:40:28 +0000
Subject: [PATCH 2/4] Use rpc_smoke_test and omit MSVC for shared_rpc_test.

---
 libc/test/shared/CMakeLists.txt      |  2 +
 libc/test/shared/shared_rpc_test.cpp | 70 +++++++++++++++++++---------
 2 files changed, 49 insertions(+), 23 deletions(-)

diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt
index 87bbd6bf448c5..4a5e8c30399b0 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -288,6 +288,7 @@ add_fp_unittest(
     libc.src.__support.str_to_integer
 )
 
+if(NOT MSVC)
 add_fp_unittest(
   shared_rpc_test
   SUITE
@@ -300,3 +301,4 @@ add_fp_unittest(
     libc.src.stdio.printf_core.writer
     libc.src.__support.arg_list
 )
+endif()
diff --git a/libc/test/shared/shared_rpc_test.cpp b/libc/test/shared/shared_rpc_test.cpp
index 0697d938a4db9..52d63140468a0 100644
--- a/libc/test/shared/shared_rpc_test.cpp
+++ b/libc/test/shared/shared_rpc_test.cpp
@@ -10,35 +10,59 @@
 #include "shared/rpc_server.h"
 #include "test/UnitTest/Test.h"
 
-namespace LIBC_NAMESPACE_DECL {
-
 TEST(LlvmLibcSharedRpcTest, TestIncrement) {
-  constexpr uint32_t port_count = 1;
-  constexpr uint32_t lane_size = 1;
+  constexpr uint32_t port_count = 8;
+  constexpr uint32_t lane_size = 4;
   alignas(64)
       uint8_t buffer[::rpc::Server::allocation_size(lane_size, port_count)];
 
-  ::rpc::Server server(port_count, buffer);
-  ::rpc::Client client(port_count, buffer);
+  using ProcAType = ::rpc::Process<false>;
+  using ProcBType = ::rpc::Process<true>;
 
-  // Client side: open a port and send data
-  auto client_port = client.open<LIBC_TEST_INCREMENT>();
-  uint64_t data = 42;
-  client_port.send(
-      [&](::rpc::Buffer *buffer, uint32_t) { buffer->data[0] = data; });
+  ProcAType ProcA(port_count, buffer);
+  ProcBType ProcB(port_count, buffer);
 
-  // Server side: handle the port
-  auto server_port = server.try_open(lane_size);
-  ASSERT_TRUE(server_port.has_value());
-  ::rpc::RPCStatus status =
-      shared::handle_libc_opcodes(*server_port, lane_size);
-  EXPECT_EQ(status, ::rpc::RPC_SUCCESS);
+  uint32_t index = 0; // any < port_count
+  uint64_t lane_mask = 1;
 
-  // Client side: receive the response
-  client_port.recv(
-      [&](::rpc::Buffer *buffer, uint32_t) { data = buffer->data[0]; });
+  // Each process has its own local lock for index
+  EXPECT_TRUE(ProcA.try_lock(lane_mask, index));
+  EXPECT_TRUE(ProcB.try_lock(lane_mask, index));
 
-  EXPECT_EQ(data, 43UL);
-}
+  // All zero to begin with
+  EXPECT_EQ(ProcA.load_inbox(lane_mask, index), 0u);
+  EXPECT_EQ(ProcB.load_inbox(lane_mask, index), 0u);
+  EXPECT_EQ(ProcA.load_outbox(lane_mask, index), 0u);
+  EXPECT_EQ(ProcB.load_outbox(lane_mask, index), 0u);
+
+  // Available for ProcA and not for ProcB
+  EXPECT_FALSE(ProcA.buffer_unavailable(ProcA.load_inbox(lane_mask, index),
+                                        ProcA.load_outbox(lane_mask, index)));
+  EXPECT_TRUE(ProcB.buffer_unavailable(ProcB.load_inbox(lane_mask, index),
+                                       ProcB.load_outbox(lane_mask, index)));
+
+  // ProcA write to outbox
+  uint32_t ProcAOutbox = ProcA.load_outbox(lane_mask, index);
+  EXPECT_EQ(ProcAOutbox, 0u);
+  ProcAOutbox = ProcA.invert_outbox(lane_mask, index, ProcAOutbox);
+  EXPECT_EQ(ProcAOutbox, 1u);
+
+  // No longer available for ProcA
+  EXPECT_TRUE(ProcA.buffer_unavailable(ProcA.load_inbox(lane_mask, index),
+                                       ProcAOutbox));
 
-} // namespace LIBC_NAMESPACE_DECL
+  // Outbox is still zero, hasn't been written to
+  EXPECT_EQ(ProcB.load_outbox(lane_mask, index), 0u);
+
+  // Wait for ownership will terminate because load_inbox returns 1
+  EXPECT_EQ(ProcB.load_inbox(lane_mask, index), 1u);
+  ProcB.wait_for_ownership(lane_mask, index, 0u, 0u);
+
+  // and B now has the buffer available
+  EXPECT_FALSE(ProcB.buffer_unavailable(ProcB.load_inbox(lane_mask, index),
+                                        ProcB.load_outbox(lane_mask, index)));
+
+  // Enough checks for one test, close the locks
+  ProcA.unlock(lane_mask, index);
+  ProcB.unlock(lane_mask, index);
+}

>From a57eff5395e33ca5cfa6c9a403c986546b17e248 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 3 Apr 2026 18:55:03 +0000
Subject: [PATCH 3/4] Zeroing buffer for rpc test.

---
 libc/test/shared/shared_rpc_test.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libc/test/shared/shared_rpc_test.cpp b/libc/test/shared/shared_rpc_test.cpp
index 52d63140468a0..69861dd102603 100644
--- a/libc/test/shared/shared_rpc_test.cpp
+++ b/libc/test/shared/shared_rpc_test.cpp
@@ -14,7 +14,8 @@ TEST(LlvmLibcSharedRpcTest, TestIncrement) {
   constexpr uint32_t port_count = 8;
   constexpr uint32_t lane_size = 4;
   alignas(64)
-      uint8_t buffer[::rpc::Server::allocation_size(lane_size, port_count)];
+      uint8_t buffer[::rpc::Server::allocation_size(lane_size, port_count)] = {
+          0};
 
   using ProcAType = ::rpc::Process<false>;
   using ProcBType = ::rpc::Process<true>;

>From cbde599b3fb30c508512b278e07682a90ed66647 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 3 Apr 2026 19:37:51 +0000
Subject: [PATCH 4/4] Fix inf_nan_converter.

---
 libc/src/stdio/printf_core/float_dec_converter.h         | 2 +-
 libc/src/stdio/printf_core/float_dec_converter_limited.h | 2 +-
 libc/src/stdio/printf_core/float_hex_converter.h         | 5 -----
 libc/src/stdio/printf_core/float_inf_nan_converter.h     | 4 ++--
 4 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index b15d7c6e04596..509b4e24ada32 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -247,7 +247,7 @@ template <WriteMode write_mode> class FloatWriter {
   // -exponent will never overflow because all long double types we support
   // have at most 15 bits of mantissa and the C standard defines an int as
   // being at least 16 bits.
-#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   static_assert(fputil::FPBits<long double>::EXP_LEN < (sizeof(int) * 8));
 #endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 
diff --git a/libc/src/stdio/printf_core/float_dec_converter_limited.h b/libc/src/stdio/printf_core/float_dec_converter_limited.h
index 6c90cf4404fcb..6b0e37b987e9a 100644
--- a/libc/src/stdio/printf_core/float_dec_converter_limited.h
+++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h
@@ -63,7 +63,7 @@ enum class ConversionType { E, F, G };
 using StorageType = UInt128;
 #else
 using StorageType = fputil::FPBits<long double>::StorageType;
-#endif
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 
 constexpr unsigned MAX_DIGITS = 39;
 constexpr size_t DF_BITS = 320;
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index 0357dbd4d3c4c..5c6899156ccba 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -92,13 +92,8 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
   // Since the number is in bits, we divide by 4, and then add one to account
   // for the extra implicit bit. We use the larger of the two possible values
   // since the size must be constant.
-#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   constexpr size_t MANT_BUFF_LEN =
       (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
-#else
-  constexpr size_t MANT_BUFF_LEN =
-      (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
-#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   char mant_buffer[MANT_BUFF_LEN];
 
   size_t mant_len = (fraction_bits / BITS_IN_HEX_DIGIT) + 1;
diff --git a/libc/src/stdio/printf_core/float_inf_nan_converter.h b/libc/src/stdio/printf_core/float_inf_nan_converter.h
index e3333978f2194..379b7c6d3dcd6 100644
--- a/libc/src/stdio/printf_core/float_inf_nan_converter.h
+++ b/libc/src/stdio/printf_core/float_inf_nan_converter.h
@@ -35,14 +35,14 @@ LIBC_INLINE int convert_inf_nan(Writer<write_mode> *writer,
   // the appropriate case based on the case of the conversion.
   bool is_negative;
   StorageType mantissa;
-#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+#ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   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);
     is_negative = float_bits.is_neg();
     mantissa = float_bits.get_mantissa();
   } else
-#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+#endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   {
     fputil::FPBits<double>::StorageType float_raw =
         static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);



More information about the libc-commits mailing list