This is an archive of the discontinued LLVM Phabricator instance.

[libc] add uint128 implementation
ClosedPublic

Authored by michaelrj on May 4 2022, 1:39 PM.

Details

Summary

Some platforms don't support proper 128 bit integers, but some
algorithms use them, such as any that use long doubles. This patch
modifies the existing UInt class to support the necessary operators.
This does not put this new class into use, that will be in followup
patches.

Diff Detail

Event Timeline

michaelrj created this revision.May 4 2022, 1:39 PM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptMay 4 2022, 1:39 PM
michaelrj requested review of this revision.May 4 2022, 1:39 PM
michaelrj updated this revision to Diff 427122.

remove some pieces of code left over from initial testing.

lntue added inline comments.May 4 2022, 3:34 PM
libc/src/__support/UInt128.h
41 ↗(On Diff #427122)

other should either be UInt128 or const UInt128&. You can also add const after the declaration:
UInt128 operator+(UInt128 other) const {. Same for operator*.

80 ↗(On Diff #427122)

Drop the const for amount and add const at the end:
UInt128 operator<<(int amount) const {. Same for other operators.

87 ↗(On Diff #427122)

BITS_IN_UINT64 is unsigned, mixing with signed amount might trigger UB / warnings.

libc/test/src/__support/uint128_test.cpp
14

You should be able to add specialization for UInt128 in LibcTest.cpp and use ASSERT_EQ, EXPECT_EQ as usual. Also disable the __uint128_t variant over there when it's not defined.

michaelrj updated this revision to Diff 427162.May 4 2022, 4:38 PM
michaelrj marked 4 inline comments as done.

make every UInt128 function constexpr and const.

libc/src/__support/UInt128.h
87 ↗(On Diff #427122)

it theoretically could, although the bigger problem was the algorithm being wrong for numbers above 64. I've fixed both shifts.

libc/test/src/__support/uint128_test.cpp
14

I could make ASSERT_EQ support my custom UInt128, but that would add a dependency for effectively no gain, since UInt128 shouldn't be called by name in any other test file. Also, the __uint128_t code is already disabled for LibcTest.cpp

We already have a more generic UInt class here: https://github.com/llvm/llvm-project/blob/main/libc/src/__support/FPUtil/UInt.h

It does not have all the operations exactly as you want, but I could add straightforward extensions and the tests you have in this patch all pass. I strongly suggest adding such extensions to the existing class instead of re-implementing a new class.

I'm not sure that extending UInt.h is a good idea, since it adds a lot of complexity for not a lot of benefit. If we had actual uses for integers larger than 128 bits, then I would agree, but right now the UInt class is not used in any entrypoint. There is a use of 192 bit integers in src/__support/FPUtil/XFloat.h which is included in src/math/generic/dp_trig.cpp, but that's never actually used. To move these functions to UInt would involve rewriting all of these functions to support arbitrary sized integers when in reality there's only one size used. This would be easy for most things, but would be very difficult for multiplying two 128 bit integers. Finally, The designs of these classes are different, my UInt128 class is intended to be an immutable number, acting similar to a primitive. The UInt class acts more like a proper class, its functions take an argument and modify the object instead of creating a new object to be returned. These are both valid designs, but mine is closer to what's expected when using __uint128_t, which is I think what's most important.

I'm not sure that extending UInt.h is a good idea, since it adds a lot of complexity for not a lot of benefit.

If you are starting out, and need only 128 bit integers, I agree with this. The point I am making is, a much general class is already present.

If we had actual uses for integers larger than 128 bits, then I would agree, but right now the UInt class is not used in any entrypoint.

I agree its not used in an entrypoint. But, that doesn't mean its incorrect or totally unused. It is actually tested in one range reduction function.

To move these functions to UInt would involve rewriting all of these functions to support arbitrary sized integers when in reality there's only one size used.

My point is to not move any of the complex pieces. Those complex parts are already implemented in very a general fashion.

Finally, The designs of these classes are different, my UInt128 class is intended to be an immutable number, acting similar to a primitive.

That is why I said that all you need is to add a few straightforward methods. Before I actually made the comment, I tried it out to actually convince myself that I was talking sense. With just a few additional but straightforward methods, all your tests work as is. For example, for addition, all I had to add was this:

UInt<Bits> operator+(const UInt<Bits> &other) const {
  UInt<Bits> result(*this);
  result.add(other);
  return result;
}

Similarly, I added straightforward methods for multiplication, shift and bitwise operators also.

michaelrj edited the summary of this revision. (Show Details)May 10 2022, 11:25 AM

changed to use the existing uint class, much of this change was contributed by sivachandra

lntue added inline comments.May 10 2022, 4:44 PM
libc/src/__support/CMakeLists.txt
63 ↗(On Diff #428444)

Is this still needed?

libc/src/__support/FPUtil/UInt.h
0

Add suffix -u/-U to the constant.

0

can this be all caps MASK32?

0

Move this out of the class.

0

Move this out of the class. You can keep them in a separate namespace of use different function name so that it won't clash with the usual strlen.

0

If you remove trivial implementations of the default and copy constructors, does it still work correctly?

2

init method is only used here, why don't we just leave its implementation here and remove the private init method?

2

This line is not needed. result was 0-initialized, and multiply by other[0] would should still be 0.

4

Remove this if it's not needed.

sivachandra added inline comments.May 10 2022, 10:57 PM
libc/src/__support/FPUtil/UInt.h
0

The hexval and strlen methods should be removed along with the commented out constructor. Even the InvalidHexDigit member.

0

Why have these been moved out of the class? They are supposed to be internal conveniences and should not be made available from a "public" namespace. If you moved them out for use in the operator* specialization, then if you make them them static, they can still live as private members.

libc/test/src/__support/uint128_test.cpp
14

By "disabled" do you mean the conditional on __SIZEOF_INT128__? If yes, you should add the else branches for it with this specialization. To keep everything more logically organized, you should probably move the UInt.h header file to src/__support/CPP.

michaelrj updated this revision to Diff 428788.May 11 2022, 3:18 PM
michaelrj marked 11 inline comments as done.

move UInt to CPP
add UInt comparison to LibcTest
Move tests to use default comparison

michaelrj marked an inline comment as done.May 11 2022, 3:20 PM
michaelrj added inline comments.
libc/src/__support/FPUtil/UInt.h
0

No, it throws errors in the tests.

libc/test/src/__support/uint128_test.cpp
14

I've moved UInt.h into CPP, and I've added specializations to LibcTest, but they're not conditional since this class is always available.

sivachandra accepted this revision.May 11 2022, 10:42 PM
This revision is now accepted and ready to land.May 11 2022, 10:42 PM
lntue accepted this revision.May 12 2022, 4:29 AM
This revision was automatically updated to reflect the committed changes.
michaelrj marked an inline comment as done.