This is an archive of the discontinued LLVM Phabricator instance.

[ConstantRange] Add srem() support
ClosedPublic

Authored by nikic on Apr 26 2019, 12:06 PM.

Details

Summary

Add support for srem() to ConstantRange so we can use it in LVI. For srem the sign of the result matches the sign of the LHS. For the RHS only the absolute value is important. Apart from that the logic is like urem.

Just like for urem this is only an approximate implementation. The tests check a few specific cases and run an exhaustive test for conservative correctness (but not exactness).

Diff Detail

Repository
rL LLVM

Event Timeline

nikic created this revision.Apr 26 2019, 12:06 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 26 2019, 12:06 PM
lebedev.ri added inline comments.Apr 26 2019, 3:07 PM
llvm/unittests/IR/ConstantRangeTest.cpp
127 ↗(On Diff #196895)

Hm, i wonder if we can also check that the result
is exact when the ranges contain a single element?
Not sure if that would hold for the current implementations though.

I also wonder if some kind of a "precision" metric should be calculated,
e.g. ConstantRange returned range with 75 elements, and exhaustive test
returned range with 67 elements then let's say it's 67/75.
And then just average that metric over all the checked ranges.

nikic marked an inline comment as done.Apr 27 2019, 1:41 AM
nikic added inline comments.
llvm/unittests/IR/ConstantRangeTest.cpp
127 ↗(On Diff #196895)

Hm, i wonder if we can also check that the result
is exact when the ranges contain a single element?
Not sure if that would hold for the current implementations though.

Unfortunately the current implementation (also the urem implementation) is not exact for single element ranges, even if both ranges only have a single element. For example 15 % 10 will return [0, 9] instead of 5.

The basic problem is that if the LHS is larger than the RHS, we have to perform a modular reduction so that [12, 18] % 10 maps onto [2, 8]. However, if both ends of the range do not divide to the same value, then we get back the full modulus range, such as [18, 22] % 10 being [0, 9], because 18 / 10 == 1 while 24 / 10 == 2.

For a single-element modulus this would be easy enough to handle, but once we get a proper range, I'm not sure how to do this, or if it's even possible to do it efficiently.

Special handling for the single-element case would definitely be possible, I'm just not sure if it's a good idea (I don't think I've seen any of the other constant range code do that).

I also wonder if some kind of a "precision" metric should be calculated,
e.g. ConstantRange returned range with 75 elements, and exhaustive test
returned range with 67 elements then let's say it's 67/75.
And then just average that metric over all the checked ranges.

What would be the goal of the metric? To make sure that the "precision" never becomes lower if the implementation is changed?

lebedev.ri added inline comments.Apr 27 2019, 3:23 AM
llvm/unittests/IR/ConstantRangeTest.cpp
127 ↗(On Diff #196895)

Special handling for the single-element case would definitely be possible, I'm just not sure if it's a good idea (I don't think I've seen any of the other constant range code do that).

Yep, i was *not* suggesting special-casing single-element ranges.

What would be the goal of the metric? To make sure that the "precision" never becomes lower if the implementation is changed?

That, or to gauge which operation needs more work.
Just a thought, i'm not sure how useful it would be.

lebedev.ri accepted this revision.May 1 2019, 1:08 PM

Ok, LG as conservative implementation.

This revision is now accepted and ready to land.May 1 2019, 1:08 PM
This revision was automatically updated to reflect the committed changes.
nikic marked an inline comment as done.May 6 2019, 10:43 AM
nikic added inline comments.
llvm/unittests/IR/ConstantRangeTest.cpp
127 ↗(On Diff #196895)

I gave the precision metric (with your suggested definition) a try and checked it against a couple of bit widths. This gave me 90% (3 bits), 95% (4 bits) and 97% (5 bits) precision.

Not sure what exactly that tells us though ^^ This is probably something that needs to be evaluated over the actual probability density of the input, otherwise we end up biasing heavily in favor of ranges with little practical relevance (at least one operand will the wrapping in 75% of the cases).