Skip to content

Commit f54208e

Browse files
committed
[libc][stdfix] Implement fxdivi functions
Signed-off-by: Shreeyash Pandey <[email protected]>
1 parent 8bf105c commit f54208e

File tree

10 files changed

+210
-0
lines changed

10 files changed

+210
-0
lines changed

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
986986
libc.src.stdfix.idivulr
987987
libc.src.stdfix.idivuk
988988
libc.src.stdfix.idivulk
989+
libc.src.stdfix.rdivi
989990
)
990991
endif()
991992

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
10191019
libc.src.stdfix.idivulr
10201020
libc.src.stdfix.idivuk
10211021
libc.src.stdfix.idivulk
1022+
libc.src.stdfix.rdivi
10221023
)
10231024
endif()
10241025

libc/include/stdfix.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,3 +544,11 @@ functions:
544544
arguments:
545545
- type: unsigned long accum
546546
guard: LIBC_COMPILER_HAS_FIXED_POINT
547+
- name: rdivi
548+
standards:
549+
- stdc_ext
550+
return_type: fract
551+
arguments:
552+
- type: int
553+
- type: int
554+
guard: LIBC_COMPILER_HAS_FIXED_POINT

libc/src/__support/fixed_point/fx_bits.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,59 @@ idiv(T x, T y) {
224224
return static_cast<XType>(result);
225225
}
226226

227+
LIBC_INLINE long accum nrstep(long accum d, long accum x0) {
228+
auto v = x0 * (2.lk - (d * x0));
229+
return v;
230+
}
231+
232+
/* Divide the two integers and return a fixed_point value
233+
*
234+
* For reference, see:
235+
* https://en.wikipedia.org/wiki/Division_algorithm#Newton%E2%80%93Raphson_division
236+
* https://stackoverflow.com/a/9231996
237+
*/
238+
template <typename XType> LIBC_INLINE constexpr XType divi(int n, int d) {
239+
// If the value of the second operand of the / operator is zero, the
240+
// behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16
241+
LIBC_CRASH_ON_VALUE(d, 0);
242+
243+
if (LIBC_UNLIKELY(n == 0)) {
244+
return FXRep<XType>::ZERO();
245+
}
246+
bool d_is_signed = false;
247+
if (d < 0) {
248+
d = (d * -1);
249+
d_is_signed = true;
250+
}
251+
252+
unsigned int nv = static_cast<unsigned int>(n);
253+
unsigned int dv = static_cast<unsigned int>(d);
254+
unsigned int clz = cpp::countl_zero<unsigned int>(dv) - 1;
255+
unsigned long int scaled_val = dv << clz;
256+
/* Scale denominator to be in the range of [0.5,1] */
257+
FXBits<long accum> d_scaled{scaled_val};
258+
unsigned long int scaled_val_n = nv << clz;
259+
/* Scale the numerator as much as the denominator to maintain correctness of
260+
* the original equation
261+
*/
262+
FXBits<long accum> n_scaled{scaled_val_n};
263+
long accum n_scaled_val = n_scaled.get_val();
264+
long accum d_scaled_val = d_scaled.get_val();
265+
/* x0 = (48/17) - (32/17) * d_n */
266+
long accum a = 2.8235lk; /* 48/17 */
267+
long accum b = 1.8823lk; /* 32/17 */
268+
long accum initial_approx = a - (b * d_scaled_val);
269+
long accum val = nrstep(d_scaled_val, initial_approx);
270+
val = nrstep(d_scaled_val, val);
271+
val = nrstep(d_scaled_val, val);
272+
val = nrstep(d_scaled_val, val);
273+
long accum res = n_scaled_val * val;
274+
if (d_is_signed) {
275+
res *= static_cast<XType>(-1);
276+
}
277+
return static_cast<XType>(res);
278+
}
279+
227280
} // namespace fixed_point
228281
} // namespace LIBC_NAMESPACE_DECL
229282

libc/src/stdfix/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,20 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
8989
)
9090
endforeach()
9191

92+
foreach(suffix IN ITEMS r)
93+
add_entrypoint_object(
94+
${suffix}divi
95+
HDRS
96+
${suffix}divi.h
97+
SRCS
98+
${suffix}divi.cpp
99+
COMPILE_OPTIONS
100+
${libc_opt_high_flag}
101+
DEPENDS
102+
libc.src.__support.fixed_point.fx_bits
103+
)
104+
endforeach()
105+
92106
add_entrypoint_object(
93107
uhksqrtus
94108
HDRS

libc/src/stdfix/rdivi.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation of rdivi function ---------------------------------===//
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+
#include "rdivi.h"
10+
#include "include/llvm-libc-macros/stdfix-macros.h" // fract
11+
#include "src/__support/common.h" // LLVM_LIBC_FUNCTION
12+
#include "src/__support/fixed_point/fx_bits.h" // fixed_point
13+
#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
LLVM_LIBC_FUNCTION(fract, rdivi, (int a, int b)) {
18+
return fixed_point::divi<fract>(a,b);
19+
}
20+
21+
} // namespace LIBC_NAMESPACE_DECL

libc/src/stdfix/rdivi.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for rdivi ------------------------*- 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_STDFIX_RDIVI_H
10+
#define LLVM_LIBC_SRC_STDFIX_RDIVI_H
11+
12+
#include "include/llvm-libc-macros/stdfix-macros.h"
13+
#include "src/__support/macros/config.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
fract rdivi(int a, int b);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_STDFIX_RDIVI_H

libc/test/src/stdfix/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
120120
)
121121
endforeach()
122122

123+
foreach(suffix IN ITEMS r)
124+
add_libc_test(
125+
${suffix}divi_test
126+
SUITE
127+
libc-stdfix-tests
128+
HDRS
129+
DivITest.h
130+
SRCS
131+
${suffix}divi_test.cpp
132+
DEPENDS
133+
libc.src.stdfix.${suffix}divi
134+
libc.src.__support.fixed_point.fx_bits
135+
libc.hdr.signal_macros
136+
)
137+
endforeach()
138+
123139
add_libc_test(
124140
uhksqrtus_test
125141
SUITE

libc/test/src/stdfix/DivITest.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===-- Utility class to test fxdivi functions ------------------*- 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+
#include "src/__support/CPP/type_traits.h"
10+
#include "src/__support/fixed_point/fx_bits.h"
11+
#include "src/__support/fixed_point/fx_rep.h"
12+
#include "test/UnitTest/Test.h"
13+
14+
template <typename XType> XType get_epsilon() {
15+
// TODO: raise error
16+
return 0;
17+
}
18+
template <> fract get_epsilon() { return FRACT_EPSILON; }
19+
template <> unsigned fract get_epsilon() { return UFRACT_EPSILON; }
20+
template <> long fract get_epsilon() { return LFRACT_EPSILON; }
21+
22+
template <typename XType>
23+
class DivITest : public LIBC_NAMESPACE::testing::Test {
24+
using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<XType>;
25+
using FXBits = LIBC_NAMESPACE::fixed_point::FXBits<XType>;
26+
27+
public:
28+
typedef XType (*DivIFunc)(int, int);
29+
30+
void testBasic(DivIFunc func) {
31+
XType epsilon = get_epsilon<XType>();
32+
EXPECT_LT((func(2, 3) - 0.666666r), epsilon);
33+
EXPECT_LT((func(3, 4) - 0.75r), epsilon);
34+
EXPECT_LT((func(1043, 2764) - 0.37735r), epsilon);
35+
EXPECT_LT((func(60000, 720293) - 0.083299r), epsilon);
36+
37+
EXPECT_EQ(func(128, 256), 0.5r);
38+
EXPECT_EQ(func(1, 2), 0.5r);
39+
EXPECT_EQ(func(1, 4), 0.25r);
40+
EXPECT_EQ(func(1, 8), 0.125r);
41+
EXPECT_EQ(func(1, 16), 0.0625r);
42+
43+
EXPECT_EQ(func(-1, 2), -0.5r);
44+
EXPECT_EQ(func(1, -4), -0.25r);
45+
EXPECT_EQ(func(-1, 8), -0.125r);
46+
EXPECT_EQ(func(1, -16), -0.0625r);
47+
}
48+
49+
void testSpecial(DivIFunc func) {
50+
EXPECT_EQ(func(0,10), 0.r);
51+
EXPECT_DEATH([func] { func(10, 0); }, WITH_SIGNAL(-1));
52+
EXPECT_EQ(func(-32768,32768), FRACT_MIN);
53+
EXPECT_EQ(func(32767,32768), FRACT_MAX);
54+
}
55+
};
56+
57+
#define LIST_DIVI_TESTS(Name, XType, func) \
58+
using LlvmLibc##Name##diviTest = DivITest<XType>; \
59+
TEST_F(LlvmLibc##Name##diviTest, Basic) { testBasic(&func); } \
60+
TEST_F(LlvmLibc##Name##diviTest, Special) { testSpecial(&func); } \
61+
static_assert(true, "Require semicolon.")

libc/test/src/stdfix/rdivi_test.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//===-- Unittests for rdivi -----------------------------------------------===//
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+
#include "DivITest.h"
10+
11+
#include "llvm-libc-macros/stdfix-macros.h" // fract
12+
#include "src/stdfix/rdivi.h"
13+
14+
LIST_DIVI_TESTS(r, fract, LIBC_NAMESPACE::rdivi);

0 commit comments

Comments
 (0)