kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH kvm-unit-tests] libcflat: provide long division routines
@ 2021-05-05 13:14 Paolo Bonzini
  2021-05-05 13:16 ` Paolo Bonzini
  0 siblings, 1 reply; 3+ messages in thread
From: Paolo Bonzini @ 2021-05-05 13:14 UTC (permalink / raw)
  To: kvm; +Cc: Bill Wendling

The -nostdlib flag disables the driver from adding libclang_rt.*.a
during linking. Adding a specific library to the command line such as
libgcc then causes the linker to report unresolved symbols, because the
libraries that resolve those symbols aren't automatically added.

libgcc however is only needed for long division (64-bit by 64-bit).
Instead of linking the whole of it, implement the routines that are
needed.

Reported-by: Bill Wendling <morbo@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 arm/Makefile.arm  |   1 +
 lib/ldiv32.c      | 105 ++++++++++++++++++++++++++++++++++++++++++++++
 x86/Makefile.i386 |   2 +-
 3 files changed, 107 insertions(+), 1 deletion(-)
 create mode 100644 lib/ldiv32.c

diff --git a/arm/Makefile.arm b/arm/Makefile.arm
index d379a28..687a8ed 100644
--- a/arm/Makefile.arm
+++ b/arm/Makefile.arm
@@ -23,6 +23,7 @@ cstart.o = $(TEST_DIR)/cstart.o
 cflatobjs += lib/arm/spinlock.o
 cflatobjs += lib/arm/processor.o
 cflatobjs += lib/arm/stack.o
+cflatobjs += lib/ldiv32.o
 
 # arm specific tests
 tests =
diff --git a/lib/ldiv32.c b/lib/ldiv32.c
new file mode 100644
index 0000000..e9d434f
--- /dev/null
+++ b/lib/ldiv32.c
@@ -0,0 +1,105 @@
+#include <inttypes.h>
+
+extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *p_rem);
+extern int64_t __moddi3(int64_t num, int64_t den);
+extern int64_t __divdi3(int64_t num, int64_t den);
+extern uint64_t __udivdi3(uint64_t num, uint64_t den);
+extern uint64_t __umoddi3(uint64_t num, uint64_t den);
+
+uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *p_rem)
+{
+	uint64_t quot = 0;
+
+	/* Trigger a division by zero at run time (trick taken from iPXE).  */
+	if (den == 0)
+		return 1/((unsigned)den);
+
+	if (num >= den) {
+		/* Align den to num to avoid wasting time on leftmost zero bits.  */
+		int n = __builtin_clzll(den) - __builtin_clzll(num);
+		den <<= n;
+
+		do {
+			quot <<= 1;
+			if (num >= den) {
+				num -= den;
+				quot |= 1;
+			}
+			den >>= 1;
+		} while (n--);
+	}
+
+	if (p_rem)
+		*p_rem = num;
+
+	return quot;
+}
+
+int64_t __moddi3(int64_t num, int64_t den)
+{
+	uint64_t mask = num < 0 ? -1 : 0;
+
+	/* Compute absolute values and do an unsigned division.  */
+	num = (num + mask) ^ mask;
+	if (den < 0)
+		den = -den;
+
+	/* Copy sign of num into result.  */
+	return (__umoddi3(num, den) + mask) ^ mask;
+}
+
+int64_t __divdi3(int64_t num, int64_t den)
+{
+	uint64_t mask = (num ^ den) < 0 ? -1 : 0;
+
+	/* Compute absolute values and do an unsigned division.  */
+	if (num < 0)
+		num = -num;
+	if (den < 0)
+		den = -den;
+
+	/* Copy sign of num^den into result.  */
+	return (__udivdi3(num, den) + mask) ^ mask;
+}
+
+uint64_t __udivdi3(uint64_t num, uint64_t den)
+{
+	uint64_t rem;
+	return __udivmoddi4(num, den, &rem);
+}
+
+uint64_t __umoddi3(uint64_t num, uint64_t den)
+{
+	uint64_t rem;
+	__udivmoddi4(num, den, &rem);
+	return rem;
+}
+
+#ifdef TEST
+#include <assert.h>
+#define UTEST(a, b, q, r) assert(__udivdi3(a, b) == q && __umoddi3(a, b) == r)
+#define STEST(a, b, q, r) assert(__divdi3(a, b) == q && __moddi3(a, b) == r)
+int main()
+{
+	UTEST(1, 1, 1, 0);
+	UTEST(2, 2, 1, 0);
+	UTEST(5, 3, 1, 2);
+	UTEST(10, 3, 3, 1);
+	UTEST(120, 3, 40, 0);
+	UTEST(120, 1, 120, 0);
+	UTEST(0x7FFFFFFFFFFFFFFFULL, 17, 0x787878787878787, 8);
+	UTEST(0x7FFFFFFFFFFFFFFFULL, 0x787878787878787, 17, 8);
+	UTEST(0x8000000000000001ULL, 17, 0x787878787878787, 10);
+	UTEST(0x8000000000000001ULL, 0x787878787878787, 17, 10);
+	UTEST(0, 5, 0, 0);
+
+	STEST(0x7FFFFFFFFFFFFFFFULL, 17, 0x787878787878787, 8);
+	STEST(0x7FFFFFFFFFFFFFFFULL, -17, -0x787878787878787, 8);
+	STEST(-0x7FFFFFFFFFFFFFFFULL, 17, -0x787878787878787, -8);
+	STEST(-0x7FFFFFFFFFFFFFFFULL, -17, 0x787878787878787, -8);
+	STEST(33, 5, 6, 3);
+	STEST(33, -5, -6, 3);
+	STEST(-33, 5, -6, -3);
+	STEST(-33, -5, 6, -3);
+}
+#endif
diff --git a/x86/Makefile.i386 b/x86/Makefile.i386
index c04e5aa..960e274 100644
--- a/x86/Makefile.i386
+++ b/x86/Makefile.i386
@@ -3,7 +3,7 @@ bits = 32
 ldarch = elf32-i386
 COMMON_CFLAGS += -mno-sse -mno-sse2
 
-cflatobjs += lib/x86/setjmp32.o
+cflatobjs += lib/x86/setjmp32.o lib/ldiv32.o
 
 tests = $(TEST_DIR)/taskswitch.flat $(TEST_DIR)/taskswitch2.flat \
 	$(TEST_DIR)/cmpxchg8b.flat $(TEST_DIR)/la57.flat
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-05-05 19:46 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-05 13:14 [PATCH kvm-unit-tests] libcflat: provide long division routines Paolo Bonzini
2021-05-05 13:16 ` Paolo Bonzini
2021-05-05 19:46   ` Bill Wendling

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).