-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[libc][stdfix] Implement fxdivi functions (rdivi) #154914
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
base: main
Are you sure you want to change the base?
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-libc Author: Shreeyash Pandey (bojle) ChangesThis PR includes only one of the fxdivi functions (rdivi). It uses a polynomial function for initial approximation followed by 4 newton-raphson iterations to calculate the reciprocal and finally multiplies the numerator with it to get the result. Full diff: https://github.com/llvm/llvm-project/pull/154914.diff 6 Files Affected:
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 1a03683d72e61..b783dd4b04c74 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -986,6 +986,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.idivulr
libc.src.stdfix.idivuk
libc.src.stdfix.idivulk
+ libc.src.stdfix.rdivi
)
endif()
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 5590f1a15ac57..f6beccc7229dd 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1019,6 +1019,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.idivulr
libc.src.stdfix.idivuk
libc.src.stdfix.idivulk
+ libc.src.stdfix.rdivi
)
endif()
diff --git a/libc/include/stdfix.yaml b/libc/include/stdfix.yaml
index 5b385124eb63d..451330c3478d2 100644
--- a/libc/include/stdfix.yaml
+++ b/libc/include/stdfix.yaml
@@ -544,3 +544,11 @@ functions:
arguments:
- type: unsigned long accum
guard: LIBC_COMPILER_HAS_FIXED_POINT
+ - name: rdivi
+ standards:
+ - stdc_ext
+ return_type: fract
+ arguments:
+ - type: int
+ - type: int
+ guard: LIBC_COMPILER_HAS_FIXED_POINT
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 00c6119b4f353..13eecba981664 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -224,6 +224,47 @@ idiv(T x, T y) {
return static_cast<XType>(result);
}
+inline long accum nrstep(long accum d, long accum x0) {
+ auto v = x0 * (2 - (d * x0));
+ return v;
+}
+
+/* Divide the two integers and return a fixed_point value
+ *
+ * For reference, see:
+ * https://en.wikipedia.org/wiki/Division_algorithm#Newton%E2%80%93Raphson_division
+ * https://stackoverflow.com/a/9231996
+ */
+template <typename XType> LIBC_INLINE constexpr XType divi(int n, int d) {
+ // If the value of the second operand of the / operator is zero, the
+ // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16
+ LIBC_CRASH_ON_VALUE(d, 0);
+
+ unsigned int nv = static_cast<unsigned int>(n);
+ unsigned int dv = static_cast<unsigned int>(d);
+ unsigned int clz = cpp::countl_zero<unsigned int>(dv) - 1;
+ unsigned long int scaled_val = dv << clz;
+ /* Scale denominator to be in the range of [0.5,1] */
+ FXBits<long accum> d_scaled{scaled_val};
+ unsigned long int scaled_val_n = nv << clz;
+ /* Scale the numerator as much as the denominator to maintain correctness of
+ * the original equation
+ */
+ FXBits<long accum> n_scaled{scaled_val_n};
+ long accum n_scaled_val = n_scaled.get_val();
+ long accum d_scaled_val = d_scaled.get_val();
+ /* x0 = (48/17) - (32/17) * d_n */
+ long accum a = 2.8235lk; /* 48/17 */
+ long accum b = 1.8823lk; /* 32/17 */
+ long accum initial_approx = a - (b * d_scaled_val);
+ long accum val = nrstep(d_scaled_val, initial_approx);
+ val = nrstep(d_scaled_val, val);
+ val = nrstep(d_scaled_val, val);
+ val = nrstep(d_scaled_val, val);
+ long accum res = n_scaled_val * val;
+ return static_cast<XType>(res);
+}
+
} // namespace fixed_point
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/CMakeLists.txt b/libc/src/stdfix/CMakeLists.txt
index 843111e3f80b1..3cbabd17ad34c 100644
--- a/libc/src/stdfix/CMakeLists.txt
+++ b/libc/src/stdfix/CMakeLists.txt
@@ -89,6 +89,20 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
)
endforeach()
+foreach(suffix IN ITEMS r)
+ add_entrypoint_object(
+ ${suffix}divi
+ HDRS
+ ${suffix}divi.h
+ SRCS
+ ${suffix}divi.cpp
+ COMPILE_OPTIONS
+ ${libc_opt_high_flag}
+ DEPENDS
+ libc.src.__support.fixed_point.fx_bits
+ )
+endforeach()
+
add_entrypoint_object(
uhksqrtus
HDRS
diff --git a/libc/test/src/stdfix/CMakeLists.txt b/libc/test/src/stdfix/CMakeLists.txt
index e2b4bc1805f7c..741522276feaa 100644
--- a/libc/test/src/stdfix/CMakeLists.txt
+++ b/libc/test/src/stdfix/CMakeLists.txt
@@ -120,6 +120,22 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
)
endforeach()
+foreach(suffix IN ITEMS r)
+ add_libc_test(
+ ${suffix}divi_test
+ SUITE
+ libc-stdfix-tests
+ HDRS
+ DivITest.h
+ SRCS
+ ${suffix}divi_test.cpp
+ DEPENDS
+ libc.src.stdfix.${suffix}divi
+ libc.src.__support.fixed_point.fx_bits
+ libc.hdr.signal_macros
+ )
+endforeach()
+
add_libc_test(
uhksqrtus_test
SUITE
|
@llvm/pr-subscribers-backend-risc-v Author: Shreeyash Pandey (bojle) ChangesThis PR includes only one of the fxdivi functions (rdivi). It uses a polynomial function for initial approximation followed by 4 newton-raphson iterations to calculate the reciprocal and finally multiplies the numerator with it to get the result. Full diff: https://github.com/llvm/llvm-project/pull/154914.diff 6 Files Affected:
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 1a03683d72e61..b783dd4b04c74 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -986,6 +986,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.idivulr
libc.src.stdfix.idivuk
libc.src.stdfix.idivulk
+ libc.src.stdfix.rdivi
)
endif()
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 5590f1a15ac57..f6beccc7229dd 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1019,6 +1019,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.idivulr
libc.src.stdfix.idivuk
libc.src.stdfix.idivulk
+ libc.src.stdfix.rdivi
)
endif()
diff --git a/libc/include/stdfix.yaml b/libc/include/stdfix.yaml
index 5b385124eb63d..451330c3478d2 100644
--- a/libc/include/stdfix.yaml
+++ b/libc/include/stdfix.yaml
@@ -544,3 +544,11 @@ functions:
arguments:
- type: unsigned long accum
guard: LIBC_COMPILER_HAS_FIXED_POINT
+ - name: rdivi
+ standards:
+ - stdc_ext
+ return_type: fract
+ arguments:
+ - type: int
+ - type: int
+ guard: LIBC_COMPILER_HAS_FIXED_POINT
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 00c6119b4f353..13eecba981664 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -224,6 +224,47 @@ idiv(T x, T y) {
return static_cast<XType>(result);
}
+inline long accum nrstep(long accum d, long accum x0) {
+ auto v = x0 * (2 - (d * x0));
+ return v;
+}
+
+/* Divide the two integers and return a fixed_point value
+ *
+ * For reference, see:
+ * https://en.wikipedia.org/wiki/Division_algorithm#Newton%E2%80%93Raphson_division
+ * https://stackoverflow.com/a/9231996
+ */
+template <typename XType> LIBC_INLINE constexpr XType divi(int n, int d) {
+ // If the value of the second operand of the / operator is zero, the
+ // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16
+ LIBC_CRASH_ON_VALUE(d, 0);
+
+ unsigned int nv = static_cast<unsigned int>(n);
+ unsigned int dv = static_cast<unsigned int>(d);
+ unsigned int clz = cpp::countl_zero<unsigned int>(dv) - 1;
+ unsigned long int scaled_val = dv << clz;
+ /* Scale denominator to be in the range of [0.5,1] */
+ FXBits<long accum> d_scaled{scaled_val};
+ unsigned long int scaled_val_n = nv << clz;
+ /* Scale the numerator as much as the denominator to maintain correctness of
+ * the original equation
+ */
+ FXBits<long accum> n_scaled{scaled_val_n};
+ long accum n_scaled_val = n_scaled.get_val();
+ long accum d_scaled_val = d_scaled.get_val();
+ /* x0 = (48/17) - (32/17) * d_n */
+ long accum a = 2.8235lk; /* 48/17 */
+ long accum b = 1.8823lk; /* 32/17 */
+ long accum initial_approx = a - (b * d_scaled_val);
+ long accum val = nrstep(d_scaled_val, initial_approx);
+ val = nrstep(d_scaled_val, val);
+ val = nrstep(d_scaled_val, val);
+ val = nrstep(d_scaled_val, val);
+ long accum res = n_scaled_val * val;
+ return static_cast<XType>(res);
+}
+
} // namespace fixed_point
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/CMakeLists.txt b/libc/src/stdfix/CMakeLists.txt
index 843111e3f80b1..3cbabd17ad34c 100644
--- a/libc/src/stdfix/CMakeLists.txt
+++ b/libc/src/stdfix/CMakeLists.txt
@@ -89,6 +89,20 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
)
endforeach()
+foreach(suffix IN ITEMS r)
+ add_entrypoint_object(
+ ${suffix}divi
+ HDRS
+ ${suffix}divi.h
+ SRCS
+ ${suffix}divi.cpp
+ COMPILE_OPTIONS
+ ${libc_opt_high_flag}
+ DEPENDS
+ libc.src.__support.fixed_point.fx_bits
+ )
+endforeach()
+
add_entrypoint_object(
uhksqrtus
HDRS
diff --git a/libc/test/src/stdfix/CMakeLists.txt b/libc/test/src/stdfix/CMakeLists.txt
index e2b4bc1805f7c..741522276feaa 100644
--- a/libc/test/src/stdfix/CMakeLists.txt
+++ b/libc/test/src/stdfix/CMakeLists.txt
@@ -120,6 +120,22 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
)
endforeach()
+foreach(suffix IN ITEMS r)
+ add_libc_test(
+ ${suffix}divi_test
+ SUITE
+ libc-stdfix-tests
+ HDRS
+ DivITest.h
+ SRCS
+ ${suffix}divi_test.cpp
+ DEPENDS
+ libc.src.stdfix.${suffix}divi
+ libc.src.__support.fixed_point.fx_bits
+ libc.hdr.signal_macros
+ )
+endforeach()
+
add_libc_test(
uhksqrtus_test
SUITE
|
Thanks. Could you add some tests for this? |
Oops, forgot to add them. I've pushed the tests too. Please check now. @PiJoules |
/* x0 = (48/17) - (32/17) * d_n */ | ||
long accum a = 2.8235lk; /* 48/17 */ | ||
long accum b = 1.8823lk; /* 32/17 */ | ||
long accum initial_approx = a - (b * d_scaled_val); | ||
long accum val = nrstep(d_scaled_val, initial_approx); | ||
val = nrstep(d_scaled_val, val); | ||
val = nrstep(d_scaled_val, val); | ||
val = nrstep(d_scaled_val, val); |
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.
Can you add error estimations for the initial approximation and after each Newton-Raphson step in the comments?
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've added error info. I've done it to the best of my understanding, though I could use a theoretical cross-check of the explanation i've provided.
You can test this locally with the following command:git-clang-format --diff HEAD~1 HEAD --extensions cpp,h -- libc/src/stdfix/rdivi.cpp libc/src/stdfix/rdivi.h libc/test/src/stdfix/DivITest.h libc/test/src/stdfix/rdivi_test.cpp libc/src/__support/fixed_point/fx_bits.h View the diff from clang-format here.diff --git a/libc/src/stdfix/rdivi.cpp b/libc/src/stdfix/rdivi.cpp
index 227b41287..7021a8b1c 100644
--- a/libc/src/stdfix/rdivi.cpp
+++ b/libc/src/stdfix/rdivi.cpp
@@ -15,7 +15,7 @@
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(fract, rdivi, (int a, int b)) {
- return fixed_point::divi<fract>(a,b);
+ return fixed_point::divi<fract>(a, b);
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/stdfix/DivITest.h b/libc/test/src/stdfix/DivITest.h
index 7ef9b4ff7..651b4ed2a 100644
--- a/libc/test/src/stdfix/DivITest.h
+++ b/libc/test/src/stdfix/DivITest.h
@@ -21,11 +21,13 @@ public:
typedef XType (*DivIFunc)(int, int);
void testBasic(DivIFunc func) {
- EXPECT_EQ(static_cast<XType>(func(128,256)), static_cast<XType>(0.5r));
- EXPECT_EQ(static_cast<XType>(func(2,3)), static_cast<XType>(0.666666r));
- EXPECT_EQ(static_cast<XType>(func(3,4)), static_cast<XType>(0.75r));
- EXPECT_EQ(static_cast<XType>(func(1043, 2764)), static_cast<XType>(0.37735r));
- EXPECT_EQ(static_cast<XType>(func(60000, 720293)), static_cast<XType>(0.083299r));
+ EXPECT_EQ(static_cast<XType>(func(128, 256)), static_cast<XType>(0.5r));
+ EXPECT_EQ(static_cast<XType>(func(2, 3)), static_cast<XType>(0.666666r));
+ EXPECT_EQ(static_cast<XType>(func(3, 4)), static_cast<XType>(0.75r));
+ EXPECT_EQ(static_cast<XType>(func(1043, 2764)),
+ static_cast<XType>(0.37735r));
+ EXPECT_EQ(static_cast<XType>(func(60000, 720293)),
+ static_cast<XType>(0.083299r));
}
};
|
Signed-off-by: Shreeyash Pandey <[email protected]>
Signed-off-by: Shreeyash Pandey <[email protected]>
This PR includes only one of the fxdivi functions (rdivi). It uses a polynomial function for initial approximation followed by 4 newton-raphson iterations to calculate the reciprocal and finally multiplies the numerator with it to get the result.
@lntue @PiJoules