LWN.net Logo

64-bit division on 32-bit arch - where's the compiler error?

64-bit division on 32-bit arch - where's the compiler error?

Posted Aug 26, 2011 4:59 UTC (Fri) by dtlin (✭ supporter ✭, #36537)
In reply to: 64-bit division on 32-bit arch - where's the compiler error? by pr1268
Parent article: Quotes of the week

That works in userspace thanks to libgcc, which isn't linked into the kernel.


(Log in to post comments)

64-bit division on 32-bit arch - where's the compiler error?

Posted Aug 28, 2011 3:03 UTC (Sun) by giraffedata (subscriber, #1954) [Link]

That works in userspace thanks to libgcc, which isn't linked into the kernel.

Not when I do it. Never mind pr1268's fancy Pentium IV. On my Pentium II, with GCC 2 (-O0), "unsigned long long a; a /= 2" compiles to two instructions, after loading 'a' into two 32 bit registers: 1) shrdl to shift the lower 32 bits right 1, shifting in the low bit of the upper 32 bits and 2) shrl to shift the upper 32 bits right 1.

No call to libgcc is emitted.

64-bit division on 32-bit arch - where's the compiler error?

Posted Aug 28, 2011 20:25 UTC (Sun) by robbe (guest, #16131) [Link]

Same here. I'm with pr1268 in assuming that some arch other than i386 is involved. The original mail on the btrfs list unfortunately only mentioned a "32bit box".

Or does the kernel use some compiler flags that forbid the use of the simple two shift instructions?

64-bit division on 32-bit arch - where's the compiler error?

Posted Aug 29, 2011 13:44 UTC (Mon) by blitzkrieg3 (subscriber, #57873) [Link]

It's not a call to the library, it's statically linked with the "real time function" __udivdi3. I'm not sure what real time functions are, but it sounds like they're sort of like inline functions for normal C operations.

Here's the error they were trying to fix:

> "ERROR: "__udivdi3" [fs/btrfs/btrfs.ko] undefined!

From what I can tell, the compiler can't translate: min_free /= 2; to a single operation on 32 bit, so it's replaced with the __udivdi3 call. In userspace, since it's a division by two, you get two bit shifts. In other cases, it would be two divl operations. The kernel can do a double divide by using do_div.

By not including __udiv in the 32-bit libraries, Linus is essentially forcing the programmer to:

A) realize that integer division is costly
B) Manually decide whether to bitshift, or do two divisions with do_div.

http://gcc.gnu.org/onlinedocs/gccint/Integer-library-rout...

64-bit division on 32-bit arch - where's the compiler error?

Posted Aug 30, 2011 2:34 UTC (Tue) by giraffedata (subscriber, #1954) [Link]

It's not a call to the library, it's statically linked with the "real time function" __udivdi3.

It's a statically linked call to the library. I think you mean "run time function" -- that's what the GCC manual page you point to calls it." It means a function in the GCC runtime library, i.e. libgcc.

__udivdi3() is for use when compiling for a CPU that doesn't have a divide instruction (that used to be pretty common -- divide is a pretty complex instruction; I don't know if it is common now. x86 has always had divide). But if the compiler is smart enough to recognize a divide by two and know you can do that by shifting, I would expect it not to use __udivdi3() on any CPU.

But the error message makes it clear that the compiler in this case generated a call to __udivdi3() when it created btrfs.ko. I assume, then, that this is not x86 because as we've seen, the compiler generates shift instructions instead on x86 (and even if it didn't, it would just generate a divide instruction). I also have to believe the divide-by-shifting cleverness is in a platform-dependent part of GCC and is absent for whatever platform this is.

One other thing to note: we've been saying this is problem with 64 bit numbers on a 32 bit CPU. But __udivdi3() is the divide function for "unsigned long" operands, i.e. on a 32 bit machine it is a 32 bit divide. The compiler would use __udivti3() instead if it wanted to divide 64 bit numbers.

64-bit division on 32-bit arch - where's the compiler error?

Posted Aug 30, 2011 3:34 UTC (Tue) by nybble41 (subscriber, #55106) [Link]

> ... we've been saying this is problem with 64 bit numbers on a 32 bit CPU. But __udivdi3() is the divide function for "unsigned long" operands, i.e. on a 32 bit machine it is a 32 bit divide. The compiler would use __udivti3() instead if it wanted to divide 64 bit numbers.

That's not quite correct. The preface to the runtime library section[1] of the GCC Internals manual states that the C types are "for illustrative purposes", and that "these routines take arguments and return values of a specific machine mode, not a specific C type." The Machine Modes section[2] defines SImode, DImode, and TImode as a four-byte integer, an eight-byte integer, and a 16-byte integer, respectively. In other words, in the hypothetical architecture invented to illustrate these library routines, the "int" in __udivsi3() is 32-bit, the "long" in __udivdi3() is 64-bit, and the "long long" in __udivti3() is 128-bit.

A simple test program confirms that 64-bit division generates a call to __udivdi3() in 32-bit mode. Similarly, division of unsigned __int128 values in 64-bit mode generates a call to __udivti3().

[1] http://gcc.gnu.org/onlinedocs/gccint/Libgcc.html
[2] http://gcc.gnu.org/onlinedocs/gccint/Machine-Modes.html

Copyright © 2013, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds