This is an archive of the discontinued LLVM Phabricator instance.

[ADT] add initializer list specialization for is_contained
ClosedPublic

Authored by beanz on Mar 19 2022, 1:23 PM.

Details

Summary

Adding an initializer list specialization for is_contained allows for compile-time evaluation when called with a constant or runtime evaluation for non-constant values.

This patch doesn't add any uses of this template, but that is coming in
a subsequent patch.

Diff Detail

Event Timeline

beanz created this revision.Mar 19 2022, 1:23 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 19 2022, 1:23 PM
beanz requested review of this revision.Mar 19 2022, 1:23 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 19 2022, 1:23 PM
pete accepted this revision.Mar 19 2022, 6:07 PM
pete added inline comments.
llvm/include/llvm/ADT/EnumMatcher.h
10 ↗(On Diff #416723)

Probably shouldn’t have DXIL in the comment. Otherwise LGTM

This revision is now accepted and ready to land.Mar 19 2022, 6:07 PM
arsenm added a subscriber: arsenm.Mar 20 2022, 5:58 AM
arsenm added inline comments.
llvm/include/llvm/ADT/EnumMatcher.h
1 ↗(On Diff #416723)

Missing license header

llvm/unittests/ADT/EnumMatcherTests.cpp
28 ↗(On Diff #416723)

EXPECT_TRUE

kuhar added a comment.Mar 21 2022, 6:34 AM

A few thoughts:

  1. Is the requirement for this to be constant folded by the optimizer?
  2. This could be made constexpr if we wanted.
  3. The function name suggests this handles enums only but it works with any copybale & comparable type. I think we should either restrict it to enum types or make the name more general.
  4. Since this function is not constexpr now, how about we added an overload to is_contained accepting an initializer list as the second element, e.g.: is_constained(MyEnum::A, {MyEnum::A, MyEnum::B, MyEnum::C})?
beanz added a comment.Mar 22 2022, 4:34 PM

A few thoughts:

  1. Is the requirement for this to be constant folded by the optimizer?

It isn't strictly a requirement. If the code is called with a non-constant value the optimizer will convert it to nice range checks.

  1. This could be made constexpr if we wanted.

I had another place that I wanted to use this in clang where the input parameter isn't constant.

  1. The function name suggests this handles enums only but it works with any copybale & comparable type. I think we should either restrict it to enum types or make the name more general.

That is totally reasonable. It's really a compile-time set matcher :).

  1. Since this function is not constexpr now, how about we added an overload to is_contained accepting an initializer list as the second element, e.g.: is_constained(MyEnum::A, {MyEnum::A, MyEnum::B, MyEnum::C})?

I realize this is probably a bit overkill, but isInEnumSet<Numbers, One, Two, Three, Four, Five>(Val) will actually optimize down to roughly if( Val >= One && Val <= Five) (assuming sequential values), and that really only works reliably with the template parameter unrolling.

Does the template compile to efficient code? lld/ELF has a utility which seems pretty efficient https://reviews.llvm.org/D112385#3084969

This patch doesn't add any uses of this template, but that is coming in a subsequent patch.

Usage link is usually directly mentioned to justify an addition to ADT/ or Support/.

beanz added a comment.Mar 22 2022, 8:57 PM

Does the template compile to efficient code? lld/ELF has a utility which seems pretty efficient https://reviews.llvm.org/D112385#3084969

With a constant value it will fold to true/false, with non-constant values it optimizes to minimal range checks.

Usage link is usually directly mentioned to justify an addition to ADT/ or Support/.

This is part of a commit stack. The next commit in the stack which uses it is https://reviews.llvm.org/D122081. :)

kuhar added a comment.EditedMar 22 2022, 9:09 PM
  1. This could be made constexpr if we wanted.

I had another place that I wanted to use this in clang where the input parameter isn't constant.

constexpr works with both constant-evaluated contexts and runtime calls.

  1. Since this function is not constexpr now, how about we added an overload to is_contained accepting an initializer list as the second element, e.g.: is_constained(MyEnum::A, {MyEnum::A, MyEnum::B, MyEnum::C})?

I realize this is probably a bit overkill, but isInEnumSet<Numbers, One, Two, Three, Four, Five>(Val) will actually optimize down to roughly if( Val >= One && Val <= Five) (assuming sequential values), and that really only works reliably with the template parameter unrolling.

I've checked and it seems to work just as well when passing in an array. With initializer_list, there are still some branches left. See https://godbolt.org/z/o7hKnKen5.

Overall, I'd lean towards the array or initializer_list version (either re-using is_contained or introducing a new name like is_in) to keep things more familiar and avoid recursion.

beanz added a comment.Mar 22 2022, 9:21 PM

constexpr works with both constant-evaluated contexts and runtime calls.

Fair point.

I've checked and it seems to work just as well when passing in an array. With initializer_list, there are still some branches left. See https://godbolt.org/z/o7hKnKen5.

Ah! I had tried using this with the existing is_contained definition, and it wasn’t folding. I think it was the std::find call, I can adjust.

Overall, I'd lean towards the array or initializer_list version (either re-using is_contained or introducing a new name like is_in to keep things more familiar and avoid recursion.

Makes sense to me. Will rework tomorrow.

Thank you!

beanz updated this revision to Diff 418714.Mar 28 2022, 2:56 PM

Updating to be a specialization on is_contained rather than a standalone thing.

beanz retitled this revision from [ADT] Add Enum matcher to [ADT] add initializer list specialization for is_contained.Mar 28 2022, 2:58 PM
beanz edited the summary of this revision. (Show Details)
kuhar added a comment.Mar 29 2022, 8:52 AM

Could you also add some tests that make sure this can be constant-evaluated? (e.g., with static_assert)

Also, would be good to have a couple of tests with non-enum types (e.g., some trivial type like int and some class with a copy constructor etc.)

llvm/unittests/ADT/STLExtrasTest.cpp
976

Can you add an extra test case with an empty set of things to match against?

979

These should be EXPECT_TRUE.

ASSERT_TRUE are for when you can't continue executing a test after a failure

979

Also, put the call directly inside the check: EXPECT_TRUE(is_contained(...)).

kuhar added inline comments.Mar 29 2022, 8:54 AM
llvm/include/llvm/ADT/STLExtras.h
1661

This function arguments are backwards compared to the other overloads. Could we keep it consistent?

beanz updated this revision to Diff 418919.Mar 29 2022, 10:23 AM

Updating based on feedback from @kuhar. Thank you!

kuhar accepted this revision.Mar 29 2022, 10:25 AM

LGTM, thanks for the fixes.

kuhar added inline comments.Mar 29 2022, 10:27 AM
llvm/unittests/ADT/STLExtrasTest.cpp
985–989

nit: make sure to run this through clang-format if you haven't already. It looks to me like some spaces might be missing.

kuhar added inline comments.Mar 29 2022, 10:28 AM
llvm/include/llvm/ADT/STLExtras.h
1661

nit: variable names should be capitalized in this function

beanz marked 2 inline comments as done.Mar 29 2022, 10:30 AM
This revision was landed with ongoing or failed builds.Mar 29 2022, 10:39 AM
This revision was automatically updated to reflect the committed changes.