-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
[libc][math][c23] Add f16{add,sub}f C23 math functions #96787
Conversation
cc @lntue |
@llvm/pr-subscribers-libc Author: OverMighty (overmighty) ChangesPart of #93566. Patch is 45.39 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/96787.diff 25 Files Affected:
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 746cc675e8fcd..79fa361b15190 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -506,9 +506,11 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.canonicalizef16
libc.src.math.ceilf16
libc.src.math.copysignf16
+ libc.src.math.f16addf
libc.src.math.f16divf
libc.src.math.f16fmaf
libc.src.math.f16sqrtf
+ libc.src.math.f16subf
libc.src.math.fabsf16
libc.src.math.fdimf16
libc.src.math.floorf16
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 34748ff5950ad..d94a40705b107 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -536,9 +536,11 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.canonicalizef16
libc.src.math.ceilf16
libc.src.math.copysignf16
+ libc.src.math.f16addf
libc.src.math.f16divf
libc.src.math.f16fmaf
libc.src.math.f16sqrtf
+ libc.src.math.f16subf
libc.src.math.fabsf16
libc.src.math.fdimf16
libc.src.math.floorf16
diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst
index e05149d8e1dc9..6f2b6f01e190c 100644
--- a/libc/docs/math/index.rst
+++ b/libc/docs/math/index.rst
@@ -124,10 +124,14 @@ Basic Operations
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| dsub | N/A | N/A | | N/A | | 7.12.14.2 | F.10.11 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
+| f16add | |check| | | | N/A | | 7.12.14.1 | F.10.11 |
++------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| f16div | |check| | | | N/A | | 7.12.14.4 | F.10.11 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| f16fma | |check| | | | N/A | | 7.12.14.5 | F.10.11 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
+| f16sub | |check| | | | N/A | | 7.12.14.2 | F.10.11 |
++------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| fabs | |check| | |check| | |check| | |check| | |check| | 7.12.7.3 | F.10.4.3 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| fadd | N/A | | | N/A | | 7.12.14.1 | F.10.11 |
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 651f49deef4c1..39ea8ff68bb4f 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -726,6 +726,10 @@ def StdC : StandardSpec<"stdc"> {
GuardedFunctionSpec<"setpayloadsigf16", RetValSpec<IntType>, [ArgSpec<Float16Ptr>, ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
+ GuardedFunctionSpec<"f16addf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
+
+ GuardedFunctionSpec<"f16subf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
+
GuardedFunctionSpec<"f16divf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
GuardedFunctionSpec<"f16sqrtf", RetValSpec<Float16Type>, [ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index 1e1bec676d444..fb1b22467f940 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -156,13 +156,13 @@ template <size_t Bits> struct DyadicFloat {
// d_lo is denormal, but the output is normal.
int scale_up_exponent = 1 - exp_lo;
T scale_up_factor =
- FPBits<T>::create_value(sign,
+ FPBits<T>::create_value(Sign::POS,
static_cast<output_bits_t>(
FPBits<T>::EXP_BIAS + scale_up_exponent),
IMPLICIT_MASK)
.get_val();
T scale_down_factor =
- FPBits<T>::create_value(sign,
+ FPBits<T>::create_value(Sign::POS,
static_cast<output_bits_t>(
FPBits<T>::EXP_BIAS - scale_up_exponent),
IMPLICIT_MASK)
diff --git a/libc/src/__support/FPUtil/generic/CMakeLists.txt b/libc/src/__support/FPUtil/generic/CMakeLists.txt
index 33b2564bfa087..5b8e09e5a47b4 100644
--- a/libc/src/__support/FPUtil/generic/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/generic/CMakeLists.txt
@@ -46,6 +46,25 @@ add_header_library(
libc.src.__support.macros.optimization
)
+add_header_library(
+ add_sub
+ HDRS
+ add_sub.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fenv_macros
+ libc.src.__support.CPP.algorithm
+ libc.src.__support.CPP.bit
+ libc.src.__support.CPP.type_traits
+ libc.src.__support.FPUtil.basic_operations
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.dyadic_float
+ libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.macros.attributes
+ libc.src.__support.macros.optimization
+)
+
add_header_library(
div
HDRS
diff --git a/libc/src/__support/FPUtil/generic/add_sub.h b/libc/src/__support/FPUtil/generic/add_sub.h
new file mode 100644
index 0000000000000..6d640026e2927
--- /dev/null
+++ b/libc/src/__support/FPUtil/generic/add_sub.h
@@ -0,0 +1,206 @@
+//===-- Add and subtract IEEE 754 floating-point numbers --------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/CPP/algorithm.h"
+#include "src/__support/CPP/bit.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/FPUtil/BasicOperations.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/dyadic_float.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/optimization.h"
+
+namespace LIBC_NAMESPACE::fputil::generic {
+
+template <bool IsSub, typename OutType, typename InType>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
+ cpp::is_floating_point_v<InType> &&
+ sizeof(OutType) <= sizeof(InType),
+ OutType>
+add_or_sub(InType x, InType y) {
+ using OutFPBits = FPBits<OutType>;
+ using OutStorageType = typename OutFPBits::StorageType;
+ using InFPBits = FPBits<InType>;
+ using InStorageType = typename InFPBits::StorageType;
+
+ constexpr int GUARD_BITS_LEN = 3;
+ constexpr int RESULT_FRACTION_LEN = InFPBits::FRACTION_LEN + GUARD_BITS_LEN;
+ constexpr int RESULT_MANTISSA_LEN = RESULT_FRACTION_LEN + 1;
+
+ using DyadicFloat =
+ DyadicFloat<cpp::bit_ceil(static_cast<size_t>(RESULT_MANTISSA_LEN))>;
+
+ InFPBits x_bits(x);
+ InFPBits y_bits(y);
+
+ bool is_effectively_add = (x_bits.sign() == y_bits.sign()) ^ IsSub;
+
+ if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||
+ x_bits.is_zero() || y_bits.is_zero())) {
+ if (x_bits.is_nan() || y_bits.is_nan()) {
+ if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
+ raise_except_if_required(FE_INVALID);
+
+ if (x_bits.is_quiet_nan()) {
+ InStorageType x_payload = static_cast<InStorageType>(getpayload(x));
+ if ((x_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
+ return OutFPBits::quiet_nan(x_bits.sign(),
+ static_cast<OutStorageType>(x_payload))
+ .get_val();
+ }
+
+ if (y_bits.is_quiet_nan()) {
+ InStorageType y_payload = static_cast<InStorageType>(getpayload(y));
+ if ((y_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
+ return OutFPBits::quiet_nan(y_bits.sign(),
+ static_cast<OutStorageType>(y_payload))
+ .get_val();
+ }
+
+ return OutFPBits::quiet_nan().get_val();
+ }
+
+ if (x_bits.is_inf()) {
+ if (y_bits.is_inf()) {
+ if (!is_effectively_add) {
+ raise_except_if_required(FE_INVALID);
+ return OutFPBits::quiet_nan().get_val();
+ }
+
+ return OutFPBits::inf(x_bits.sign()).get_val();
+ }
+
+ return OutFPBits::inf(x_bits.sign()).get_val();
+ }
+
+ if (y_bits.is_inf())
+ return OutFPBits::inf(y_bits.sign()).get_val();
+
+ if (x_bits.is_zero()) {
+ if (y_bits.is_zero()) {
+ switch (quick_get_round()) {
+ case FE_DOWNWARD:
+ return OutFPBits::zero(Sign::NEG).get_val();
+ default:
+ return OutFPBits::zero(Sign::POS).get_val();
+ }
+ }
+
+ // volatile prevents Clang from converting tmp to OutType and then
+ // immediately back to InType before negating it, resulting in double
+ // rounding.
+ volatile InType tmp = y;
+ if constexpr (IsSub)
+ tmp = -tmp;
+ return static_cast<OutType>(tmp);
+ }
+
+ if (y_bits.is_zero()) {
+ volatile InType tmp = y;
+ if constexpr (IsSub)
+ tmp = -tmp;
+ return static_cast<OutType>(tmp);
+ }
+ }
+
+ InType x_abs = x_bits.abs().get_val();
+ InType y_abs = y_bits.abs().get_val();
+
+ if (x_abs == y_abs && !is_effectively_add) {
+ switch (quick_get_round()) {
+ case FE_DOWNWARD:
+ return OutFPBits::zero(Sign::NEG).get_val();
+ default:
+ return OutFPBits::zero(Sign::POS).get_val();
+ }
+ }
+
+ Sign result_sign = Sign::POS;
+
+ if (x_abs > y_abs) {
+ result_sign = x_bits.sign();
+ } else if (x_abs < y_abs) {
+ if (is_effectively_add)
+ result_sign = y_bits.sign();
+ else if (y_bits.is_pos())
+ result_sign = Sign::NEG;
+ } else if (is_effectively_add) {
+ result_sign = x_bits.sign();
+ }
+
+ InFPBits max_bits(cpp::max(x_abs, y_abs));
+ InFPBits min_bits(cpp::min(x_abs, y_abs));
+
+ InStorageType result_mant;
+
+ if (max_bits.is_subnormal()) {
+ // min_bits must be subnormal too.
+
+ if (is_effectively_add)
+ result_mant = max_bits.get_mantissa() + min_bits.get_mantissa();
+ else
+ result_mant = max_bits.get_mantissa() - min_bits.get_mantissa();
+
+ result_mant <<= GUARD_BITS_LEN;
+ } else {
+ InStorageType max_mant = max_bits.get_explicit_mantissa() << GUARD_BITS_LEN;
+ InStorageType min_mant = min_bits.get_explicit_mantissa() << GUARD_BITS_LEN;
+ int alignment =
+ max_bits.get_biased_exponent() - min_bits.get_biased_exponent();
+
+ InStorageType aligned_min_mant =
+ min_mant >> cpp::min(alignment, RESULT_MANTISSA_LEN);
+ bool aligned_min_mant_sticky;
+
+ if (alignment <= 3)
+ aligned_min_mant_sticky = false;
+ else if (alignment <= InFPBits::FRACTION_LEN + 3)
+ aligned_min_mant_sticky =
+ (min_mant << (InFPBits::STORAGE_LEN - alignment)) != 0;
+ else
+ aligned_min_mant_sticky = true;
+
+ if (is_effectively_add)
+ result_mant = max_mant + (aligned_min_mant | aligned_min_mant_sticky);
+ else
+ result_mant = max_mant - (aligned_min_mant | aligned_min_mant_sticky);
+ }
+
+ int result_exp = max_bits.get_exponent() - RESULT_FRACTION_LEN;
+ DyadicFloat result(result_sign, result_exp, result_mant);
+ return result.template as<OutType, /*ShouldSignalExceptions=*/true>();
+}
+
+template <typename OutType, typename InType>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
+ cpp::is_floating_point_v<InType> &&
+ sizeof(OutType) <= sizeof(InType),
+ OutType>
+add(InType x, InType y) {
+ return add_or_sub</*IsSub=*/false, OutType>(x, y);
+}
+
+template <typename OutType, typename InType>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
+ cpp::is_floating_point_v<InType> &&
+ sizeof(OutType) <= sizeof(InType),
+ OutType>
+sub(InType x, InType y) {
+ return add_or_sub</*IsSub=*/true, OutType>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE::fputil::generic
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 711cbf8bbfdca..d3af54194b567 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -99,12 +99,16 @@ add_math_entrypoint_object(exp10f)
add_math_entrypoint_object(expm1)
add_math_entrypoint_object(expm1f)
+add_math_entrypoint_object(f16addf)
+
add_math_entrypoint_object(f16divf)
add_math_entrypoint_object(f16fmaf)
add_math_entrypoint_object(f16sqrtf)
+add_math_entrypoint_object(f16subf)
+
add_math_entrypoint_object(fabs)
add_math_entrypoint_object(fabsf)
add_math_entrypoint_object(fabsl)
diff --git a/libc/src/math/f16addf.h b/libc/src/math/f16addf.h
new file mode 100644
index 0000000000000..31d0e786584e6
--- /dev/null
+++ b/libc/src/math/f16addf.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for f16addf -----------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_F16ADDF_H
+#define LLVM_LIBC_SRC_MATH_F16ADDF_H
+
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE {
+
+float16 f16addf(float x, float y);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_F16ADDF_H
diff --git a/libc/src/math/f16subf.h b/libc/src/math/f16subf.h
new file mode 100644
index 0000000000000..1d04a4c952d17
--- /dev/null
+++ b/libc/src/math/f16subf.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for f16subf -----------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_F16SUBF_H
+#define LLVM_LIBC_SRC_MATH_F16SUBF_H
+
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE {
+
+float16 f16subf(float x, float y);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_F16SUBF_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 41a77c8710f6b..a143730aaf9bb 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -3754,6 +3754,32 @@ add_entrypoint_object(
-O3
)
+add_entrypoint_object(
+ f16addf
+ SRCS
+ f16addf.cpp
+ HDRS
+ ../f16addf.h
+ DEPENDS
+ libc.src.__support.macros.properties.types
+ libc.src.__support.FPUtil.generic.add_sub
+ COMPILE_OPTIONS
+ -O3
+)
+
+add_entrypoint_object(
+ f16subf
+ SRCS
+ f16subf.cpp
+ HDRS
+ ../f16subf.h
+ DEPENDS
+ libc.src.__support.macros.properties.types
+ libc.src.__support.FPUtil.generic.add_sub
+ COMPILE_OPTIONS
+ -O3
+)
+
add_entrypoint_object(
f16divf
SRCS
diff --git a/libc/src/math/generic/f16addf.cpp b/libc/src/math/generic/f16addf.cpp
new file mode 100644
index 0000000000000..f1761a193985d
--- /dev/null
+++ b/libc/src/math/generic/f16addf.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of f16addf function --------------------------------===//
+//
+// 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/math/f16addf.h"
+#include "src/__support/FPUtil/generic/add_sub.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(float16, f16addf, (float x, float y)) {
+ return fputil::generic::add<float16>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/math/generic/f16subf.cpp b/libc/src/math/generic/f16subf.cpp
new file mode 100644
index 0000000000000..e4532a456b7b8
--- /dev/null
+++ b/libc/src/math/generic/f16subf.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of f16subf function --------------------------------===//
+//
+// 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/math/f16subf.h"
+#include "src/__support/FPUtil/generic/add_sub.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(float16, f16subf, (float x, float y)) {
+ return fputil::generic::sub<float16>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/test/src/math/AddTest.h b/libc/test/src/math/AddTest.h
new file mode 100644
index 0000000000000..df0ef66cfeefa
--- /dev/null
+++ b/libc/test/src/math/AddTest.h
@@ -0,0 +1,74 @@
+//===-- Utility class to test different flavors of float add ----*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TEST_SRC_MATH_ADDTEST_H
+#define LLVM_LIBC_TEST_SRC_MATH_ADDTEST_H
+
+#include "test/UnitTest/FEnvSafeTest.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+template <typename OutType, typename InType>
+class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
+
+ struct InConstants {
+ DECLARE_SPECIAL_CONSTANTS(InType)
+ };
+
+ using InFPBits = typename InConstants::FPBits;
+ using InStorageType = typename InConstants::StorageType;
+
+ static constexpr InStorageType IN_MAX_NORMAL_U =
+ InFPBits::max_normal().uintval();
+ static constexpr InStorageType IN_MIN_NORMAL_U =
+ InFPBits::min_normal().uintval()...
[truncated]
|
2219e9e
to
597a713
Compare
Rebased to fix merge conflicts and added this commit:
|
597a713
to
6373f24
Compare
Rebased and fixed the merge conflict. |
} | ||
} | ||
|
||
// volatile prevents Clang from converting tmp to OutType and then |
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.
if constexpr(IsSub)
return static_cast<OutType>(-tmp);
else
return static_cast<OutType>(tmp);
does not work properly without volatile
?
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.
I didn't think of that. Will try it.
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.
That doesn't give the expected results either.
Fix double rounding error.
Replace ^ on bools with !=.
6373f24
to
087476c
Compare
Rebased to resolve the merge conflict and added this commit:
|
Part of #93566.