Skip to content

Commit 780bc8e

Browse files
committed
[libc][math][c23] Add {f,d}mul{l,f128} and f16mul{,f,l,f128} C23 math functions
Part of #93566. Fixes #94833.
1 parent da0c8b2 commit 780bc8e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1242
-364
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ set(TARGET_LIBM_ENTRYPOINTS
357357
libc.src.math.cosf
358358
libc.src.math.coshf
359359
libc.src.math.cospif
360+
libc.src.math.dmull
360361
libc.src.math.erff
361362
libc.src.math.exp
362363
libc.src.math.exp10
@@ -411,6 +412,7 @@ set(TARGET_LIBM_ENTRYPOINTS
411412
libc.src.math.fmodf
412413
libc.src.math.fmodl
413414
libc.src.math.fmul
415+
libc.src.math.fmull
414416
libc.src.math.frexp
415417
libc.src.math.frexpf
416418
libc.src.math.frexpl
@@ -531,6 +533,8 @@ if(LIBC_TYPES_HAS_FLOAT16)
531533
libc.src.math.f16div
532534
libc.src.math.f16divf
533535
libc.src.math.f16fmaf
536+
libc.src.math.f16mul
537+
libc.src.math.f16mulf
534538
libc.src.math.f16sqrt
535539
libc.src.math.f16sqrtf
536540
libc.src.math.f16sub

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ set(TARGET_LIBM_ENTRYPOINTS
382382
libc.src.math.cosf
383383
libc.src.math.coshf
384384
libc.src.math.cospif
385+
libc.src.math.dmull
385386
libc.src.math.erff
386387
libc.src.math.exp
387388
libc.src.math.exp10
@@ -437,6 +438,7 @@ set(TARGET_LIBM_ENTRYPOINTS
437438
libc.src.math.fmodf
438439
libc.src.math.fmodl
439440
libc.src.math.fmul
441+
libc.src.math.fmull
440442
libc.src.math.frexp
441443
libc.src.math.frexpf
442444
libc.src.math.frexpl
@@ -561,6 +563,9 @@ if(LIBC_TYPES_HAS_FLOAT16)
561563
libc.src.math.f16fma
562564
libc.src.math.f16fmaf
563565
libc.src.math.f16fmal
566+
libc.src.math.f16mul
567+
libc.src.math.f16mulf
568+
libc.src.math.f16mull
564569
libc.src.math.f16sqrt
565570
libc.src.math.f16sqrtf
566571
libc.src.math.f16sqrtl
@@ -622,6 +627,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
622627
libc.src.math.f16addf128
623628
libc.src.math.f16divf128
624629
libc.src.math.f16fmaf128
630+
libc.src.math.f16mulf128
625631
libc.src.math.f16sqrtf128
626632
libc.src.math.f16subf128
627633
)
@@ -634,6 +640,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
634640
libc.src.math.canonicalizef128
635641
libc.src.math.ceilf128
636642
libc.src.math.copysignf128
643+
libc.src.math.dmulf128
637644
libc.src.math.fabsf128
638645
libc.src.math.fdimf128
639646
libc.src.math.floorf128
@@ -648,6 +655,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
648655
libc.src.math.fminimum_numf128
649656
libc.src.math.fminimumf128
650657
libc.src.math.fmodf128
658+
libc.src.math.fmulf128
651659
libc.src.math.frexpf128
652660
libc.src.math.fromfpf128
653661
libc.src.math.fromfpxf128

libc/docs/math/index.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Basic Operations
120120
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
121121
| dfma | N/A | N/A | | N/A | | 7.12.14.5 | F.10.11 |
122122
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
123-
| dmul | N/A | N/A | | N/A | | 7.12.14.3 | F.10.11 |
123+
| dmul | N/A | N/A | |check| | N/A | |check|\* | 7.12.14.3 | F.10.11 |
124124
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
125125
| dsub | N/A | N/A | | N/A | | 7.12.14.2 | F.10.11 |
126126
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
@@ -130,6 +130,8 @@ Basic Operations
130130
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
131131
| f16fma | |check|\* | |check|\* | |check|\* | N/A | |check| | 7.12.14.5 | F.10.11 |
132132
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
133+
| f16mul | |check|\* | |check|\* | |check|\* | N/A | |check| | 7.12.14.5 | F.10.11 |
134+
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
133135
| f16sub | |check|\* | |check|\* | |check|\* | N/A | |check| | 7.12.14.2 | F.10.11 |
134136
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
135137
| fabs | |check| | |check| | |check| | |check| | |check| | 7.12.7.3 | F.10.4.3 |
@@ -166,7 +168,7 @@ Basic Operations
166168
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
167169
| fmod | |check| | |check| | |check| | |check| | |check| | 7.12.10.1 | F.10.7.1 |
168170
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
169-
| fmul | N/A | |check| | | N/A | | 7.12.14.3 | F.10.11 |
171+
| fmul | N/A | |check| | |check| | N/A | |check|\* | 7.12.14.3 | F.10.11 |
170172
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
171173
| frexp | |check| | |check| | |check| | |check| | |check| | 7.12.6.7 | F.10.3.7 |
172174
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+

libc/spec/llvm_libc_ext.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ def LLVMLibcExt : StandardSpec<"llvm_libc_ext"> {
6565
GuardedFunctionSpec<"f16subf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
6666
GuardedFunctionSpec<"f16subl", RetValSpec<Float16Type>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
6767

68+
GuardedFunctionSpec<"fmulf128", RetValSpec<FloatType>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT128">,
69+
70+
GuardedFunctionSpec<"dmulf128", RetValSpec<DoubleType>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT128">,
71+
72+
GuardedFunctionSpec<"f16mul", RetValSpec<Float16Type>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
73+
GuardedFunctionSpec<"f16mulf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
74+
GuardedFunctionSpec<"f16mull", RetValSpec<Float16Type>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
75+
6876
GuardedFunctionSpec<"f16div", RetValSpec<Float16Type>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
6977
GuardedFunctionSpec<"f16divf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
7078
GuardedFunctionSpec<"f16divl", RetValSpec<Float16Type>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>], "LIBC_TYPES_HAS_FLOAT16">,

libc/spec/stdc.td

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,8 +475,6 @@ def StdC : StandardSpec<"stdc"> {
475475
GuardedFunctionSpec<"fminimum_mag_numf16", RetValSpec<Float16Type>, [ArgSpec<Float16Type>, ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
476476
GuardedFunctionSpec<"fminimum_mag_numf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT128">,
477477

478-
FunctionSpec<"fmul", RetValSpec<FloatType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
479-
480478
FunctionSpec<"fma", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
481479
FunctionSpec<"fmaf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>, ArgSpec<FloatType>]>,
482480

@@ -733,6 +731,13 @@ def StdC : StandardSpec<"stdc"> {
733731

734732
GuardedFunctionSpec<"f16subf128", RetValSpec<Float16Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT16_AND_FLOAT128">,
735733

734+
FunctionSpec<"fmul", RetValSpec<FloatType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
735+
FunctionSpec<"fmull", RetValSpec<FloatType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
736+
737+
FunctionSpec<"dmull", RetValSpec<DoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
738+
739+
GuardedFunctionSpec<"f16mulf128", RetValSpec<Float16Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT16_AND_FLOAT128">,
740+
736741
GuardedFunctionSpec<"f16divf128", RetValSpec<Float16Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT16_AND_FLOAT128">,
737742

738743
GuardedFunctionSpec<"f16sqrtf128", RetValSpec<Float16Type>, [ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT16_AND_FLOAT128">,

libc/src/__support/FPUtil/generic/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,20 @@ add_header_library(
8484
libc.src.__support.macros.attributes
8585
libc.src.__support.macros.optimization
8686
)
87+
88+
add_header_library(
89+
mul
90+
HDRS
91+
mul.h
92+
DEPENDS
93+
libc.hdr.errno_macros
94+
libc.hdr.fenv_macros
95+
libc.src.__support.CPP.bit
96+
libc.src.__support.CPP.type_traits
97+
libc.src.__support.FPUtil.basic_operations
98+
libc.src.__support.FPUtil.fenv_impl
99+
libc.src.__support.FPUtil.fp_bits
100+
libc.src.__support.FPUtil.dyadic_float
101+
libc.src.__support.macros.attributes
102+
libc.src.__support.macros.optimization
103+
)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//===-- Division of IEEE 754 floating-point numbers -------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_MUL_H
10+
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_MUL_H
11+
12+
#include "hdr/errno_macros.h"
13+
#include "hdr/fenv_macros.h"
14+
#include "src/__support/CPP/bit.h"
15+
#include "src/__support/CPP/type_traits.h"
16+
#include "src/__support/FPUtil/BasicOperations.h"
17+
#include "src/__support/FPUtil/FEnvImpl.h"
18+
#include "src/__support/FPUtil/FPBits.h"
19+
#include "src/__support/FPUtil/dyadic_float.h"
20+
#include "src/__support/macros/attributes.h"
21+
#include "src/__support/macros/config.h"
22+
#include "src/__support/macros/optimization.h"
23+
24+
namespace LIBC_NAMESPACE_DECL {
25+
namespace fputil::generic {
26+
27+
template <typename OutType, typename InType>
28+
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
29+
cpp::is_floating_point_v<InType> &&
30+
sizeof(OutType) <= sizeof(InType),
31+
OutType>
32+
mul(InType x, InType y) {
33+
using OutFPBits = FPBits<OutType>;
34+
using OutStorageType = typename OutFPBits::StorageType;
35+
using InFPBits = FPBits<InType>;
36+
using InStorageType = typename InFPBits::StorageType;
37+
// The product of two p-digit numbers is a 2p-digit number.
38+
using DyadicFloat = DyadicFloat<cpp::bit_ceil(
39+
2 * static_cast<size_t>(InFPBits::FRACTION_LEN))>;
40+
using DyadicMantissaType = typename DyadicFloat::MantissaType;
41+
42+
InFPBits x_bits(x);
43+
InFPBits y_bits(y);
44+
45+
Sign result_sign = x_bits.sign() == y_bits.sign() ? Sign::POS : Sign::NEG;
46+
47+
if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||
48+
x_bits.is_zero() || y_bits.is_zero())) {
49+
if (x_bits.is_nan() || y_bits.is_nan()) {
50+
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
51+
raise_except_if_required(FE_INVALID);
52+
53+
if (x_bits.is_quiet_nan()) {
54+
InStorageType x_payload = static_cast<InStorageType>(getpayload(x));
55+
if ((x_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
56+
return OutFPBits::quiet_nan(x_bits.sign(),
57+
static_cast<OutStorageType>(x_payload))
58+
.get_val();
59+
}
60+
61+
if (y_bits.is_quiet_nan()) {
62+
InStorageType y_payload = static_cast<InStorageType>(getpayload(y));
63+
if ((y_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
64+
return OutFPBits::quiet_nan(y_bits.sign(),
65+
static_cast<OutStorageType>(y_payload))
66+
.get_val();
67+
}
68+
69+
return OutFPBits::quiet_nan().get_val();
70+
}
71+
72+
if (x_bits.is_inf()) {
73+
if (y_bits.is_zero()) {
74+
set_errno_if_required(EDOM);
75+
raise_except_if_required(FE_INVALID);
76+
return OutFPBits::quiet_nan().get_val();
77+
}
78+
79+
return OutFPBits::inf(result_sign).get_val();
80+
}
81+
82+
if (y_bits.is_inf()) {
83+
if (x_bits.is_zero()) {
84+
set_errno_if_required(EDOM);
85+
raise_except_if_required(FE_INVALID);
86+
return OutFPBits::quiet_nan().get_val();
87+
}
88+
89+
return OutFPBits::inf(result_sign).get_val();
90+
}
91+
92+
// Now either x or y is zero, and the other one is finite.
93+
return OutFPBits::zero(result_sign).get_val();
94+
}
95+
96+
DyadicFloat xd(x);
97+
DyadicFloat yd(y);
98+
99+
// The product is either in the [2, 4) range or the [1, 2) range. We initially
100+
// set the exponent as if the product were in the [2, 4) range. If the product
101+
// is in the [1, 2) range, then it has 1 leading zero bit and the DyadicFloat
102+
// constructor will normalize it and adjust the exponent.
103+
int result_exp = xd.get_unbiased_exponent() + yd.get_unbiased_exponent() + 1 -
104+
DyadicMantissaType::BITS + 1;
105+
106+
// We use a single DyadicFloat type, which can fit the 2p-digit product, for
107+
// both the normalized inputs and the product. So we need to right-shift the
108+
// normalized input mantissas before multiplying them.
109+
xd.shift_right(DyadicMantissaType::BITS / 2);
110+
yd.shift_right(DyadicMantissaType::BITS / 2);
111+
DyadicMantissaType product = xd.mantissa * yd.mantissa;
112+
113+
DyadicFloat result(result_sign, result_exp, product);
114+
return result.template as<OutType, /*ShouldSignalExceptions=*/true>();
115+
}
116+
117+
} // namespace fputil::generic
118+
} // namespace LIBC_NAMESPACE_DECL
119+
120+
#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_MUL_H

libc/src/math/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ add_math_entrypoint_object(cosh)
8686
add_math_entrypoint_object(coshf)
8787
add_math_entrypoint_object(cospif)
8888

89+
add_math_entrypoint_object(dmull)
90+
add_math_entrypoint_object(dmulf128)
91+
8992
add_math_entrypoint_object(erf)
9093
add_math_entrypoint_object(erff)
9194

@@ -118,6 +121,11 @@ add_math_entrypoint_object(f16fmaf)
118121
add_math_entrypoint_object(f16fmal)
119122
add_math_entrypoint_object(f16fmaf128)
120123

124+
add_math_entrypoint_object(f16mul)
125+
add_math_entrypoint_object(f16mulf)
126+
add_math_entrypoint_object(f16mull)
127+
add_math_entrypoint_object(f16mulf128)
128+
121129
add_math_entrypoint_object(f16sqrt)
122130
add_math_entrypoint_object(f16sqrtf)
123131
add_math_entrypoint_object(f16sqrtl)
@@ -210,6 +218,8 @@ add_math_entrypoint_object(fminimum_mag_numf16)
210218
add_math_entrypoint_object(fminimum_mag_numf128)
211219

212220
add_math_entrypoint_object(fmul)
221+
add_math_entrypoint_object(fmull)
222+
add_math_entrypoint_object(fmulf128)
213223

214224
add_math_entrypoint_object(fmod)
215225
add_math_entrypoint_object(fmodf)

libc/src/math/dmulf128.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for dmulf128 ----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_MATH_DMULF128_H
10+
#define LLVM_LIBC_SRC_MATH_DMULF128_H
11+
12+
#include "src/__support/macros/config.h"
13+
#include "src/__support/macros/properties/types.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
double dmulf128(float128 x, float128 y);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_MATH_DMULF128_H

libc/src/math/dmull.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for dmull -------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_MATH_DMULL_H
10+
#define LLVM_LIBC_SRC_MATH_DMULL_H
11+
12+
#include "src/__support/macros/config.h"
13+
14+
namespace LIBC_NAMESPACE_DECL {
15+
16+
double dmull(long double x, long double y);
17+
18+
} // namespace LIBC_NAMESPACE_DECL
19+
20+
#endif // LLVM_LIBC_SRC_MATH_DMULL_H

libc/src/math/f16mul.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for f16mul ------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_MATH_F16MUL_H
10+
#define LLVM_LIBC_SRC_MATH_F16MUL_H
11+
12+
#include "src/__support/macros/config.h"
13+
#include "src/__support/macros/properties/types.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
float16 f16mul(double x, double y);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_MATH_F16MUL_H

libc/src/math/f16mulf.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for f16mulf -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_MATH_F16MULF_H
10+
#define LLVM_LIBC_SRC_MATH_F16MULF_H
11+
12+
#include "src/__support/macros/config.h"
13+
#include "src/__support/macros/properties/types.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
float16 f16mulf(float x, float y);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_MATH_F16MULF_H

0 commit comments

Comments
 (0)